现在很多的web页面使用ajax技术动态加载页面。但对于爬虫来说,目标数据很可能不在页面HTML源码中(右键查看网页源代码,通过F12查找),针对静态页面的爬虫不再满足现在的需求。
很多教程都推荐用Selenium和PhantomJS配合使用,实现网页的渲染,得到网页的全部信息。但是对于爬虫程序,模拟浏览器内存开销实在是非常大,而且效率低。
好消息是,大多是是浏览器会在请求和解析HTML之后,根据js的“指示”再发送一次请求,得到页面展示的内容,然后通过js渲染之后展示到界面。这样的请求往往得到的内容是json格式的,所以我们非但不会加重爬虫的任务,反而可能会省去解析HTML的功夫。
本文以爬取instagram上的某位明星上传的全部图片为例讲解动态加载页面的解决办法。文末附上全部代码
工具:Chrome
包:json,requests,urllib
分析ins页面
打开某用户ins主页(https://www.instagram.com/urnotchrislee/?hl=zh-cn)可以看到,首页只加载了12张图片,要点击“更多”才会加载更多的图片。我们先获取这12张图片的URL。
获取前12张图片的URL
首先检查源码中是否存在图片的URL。在script标签中发现前12张图片的URL。
有了这个发现,我们就可以提取首页的12张图片URL!!
代码如下:
通过使用lxml库实现解析,不了解lxml的童鞋可以去崔大神的博客 学习学习。
获取更多图片的URL
在点击“更多”按键时,发现XHR选项卡中加载了一些新文件。
每一次下拉页面都会加载出12张图片,“?query_idbalabala”文件也会增加一个!
点击文件查看详细信息。发现该文件返回json数据,并且数据中包含我们要的图片URL!!
所以说只要获取到这些json数据,我们就能提取出更多的图片URL。
仔细看上图的Request URL,让我们分析下它的请求参数(包括query_id,id,first,after)可以看到后续加载的query_id都为“17888483320059182”,“id”为你所访问的用户的用户id,为“1161353543”,“first”为一次加载的图片数,始终为12。“after”比较复杂。但是按常理来说,这个参数应该在前面的文件中能够找到。
找“after”
利用ctrl+F在页面源码中找到了after参数对应的值。同样是在script标签中,不过对应的键名是“end_cursor”
凑齐了四个请求参数,这下我们可以构造Request URL,通过解析返回的json数据,获取更多的图片URL了
代码如下:
src_list中存储了该用户590张图片的URL,现在该进入到下载环节啦~
下载图片
博主懒得换代理,直接用来最简单粗暴的方式下载了图片。
结果展示~
全部代码
# -*- coding: utf-8 -*-
import json
import requests
from lxml import etree
from urllib import parse
BASE_URL = "https://www.instagram.com/urnotchrislee/"
headers = {
"Origin": "https://www.instagram.com/",
"Referer": "https://www.instagram.com/urnotchrislee/",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/58.0.3029.110 Safari/537.36",
"Host": "www.instagram.com"}
def load_rest(table,has_next_page):
rest = []
while has_next_page:
text = json.dumps(table)
URL = 'https://www.instagram.com/graphql/query/?query_id=17888483320059182&variables='+parse.quote(text)
res = requests.get(URL,headers = headers)
dic = json.loads(res.content.decode(),encoding='utf-8')
data = dic['data']['user']['edge_owner_to_timeline_media']
nodes = data['edges']
end_cursor = data['page_info']['end_cursor']
has_next_page = data['page_info']['has_next_page']
for node in nodes:
rest.append(node['node']['display_url'])
#print(node['node']['display_url'])
table['after'] = end_cursor
print('加载..')
print('加载完成')
return rest
if __name__=='__main__':
res = requests.get(BASE_URL,headers = headers)
html = etree.HTML(res.content.decode())
# h = html.xpath('''//script[@type="text/javascript"]/text()''')[1].replace('window._sharedData =','').strip()
h = html.xpath('''//script[@type="text/javascript"]''')[1].text.replace('window._sharedData = ','').strip()[:-1]
dic = json.loads(h,encoding='utf-8')
data = dic['entry_data']['ProfilePage'][0]['user']['media']
nodes = data['nodes']
end_cursor = data['page_info']['end_cursor']
has_next_page = data['page_info']['has_next_page']
lee_id = nodes[0]["owner"]["id"] #'1161353543'
src_list = []
for node in nodes:
src_list.append(node['display_src'])
print(node['display_src'])
print('加载')
table = {
'id':lee_id,
'first':12,
'after':end_cursor}
rest = load_rest(table,has_next_page)
src_list = src_list + rest
print(len(src_list))
# with open('abc', 'w') as f:
# for s in src_list:
# f.write(s)
# f.write('\n')
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/58.0.3029.110 Safari/537.36",}
for i in range(len(src_list)):
url = src_list[i].strip()
res = requests.get(url,headers = headers)
with open('第'+str(i+1)+'张.jpg','wb') as ff:
ff.write(res.content)
参考网页:
Web crawler with Python - 04.另一种抓取方式
新手小白,逻辑混乱,欢迎大佬指正错误,求轻喷