有时候我们在用requests抓取页面的时候,得到的结果可能和在浏览器中看到的不一样:在浏览器中可以看到正常显示的页面数据,但是使用requests得到的结果中并没有。这是因为requests获取的都是原始的HTML文档,而浏览器中的页面则是经过JavaScript处理数据后生成的结果,这些数据的来源有多种,可能是通过Ajax加载的,可能是包含在HTML文档中的,也可能是经过JavaScript 和特定算法计算后生成的。
对于第一种情况,数据加载是一种异步加载方式,原始的页面最初不会包含某些数据,原始页面加载完后,会再向服务器请求某个接口获取数据,然后数据才被处理从而呈现到网页上,这其实就是发送了一个ajax请求。
照Web发展的趋势来看,这种形式的页面会越来越多。网页的原始HTML文档不会包含任何数据,数据都是通过Ajax统一加载后再呈现出来的,这样在Web开发上可以做到前后端分离,而且降低服务器直接渲染页面带来的压力。
所以如果遇到这样的页面,直接利用requests等库来抓取原始页面,是无法获取到有效数据的,这时需要分析网页后台向接口发送的Ajax请求,
发送Ajax请求到网页更新的这个过程可以简单分为以下3步:
- 通过javascript的XMLHttpRequest对象向服务器发送请求
- 将服务器返回的数据进行解析和转化
- 将能够使用的数据通过javascript的DOM操作应用到原始html页面上
模拟ajax请求进行爬取首先就是要获取网页向服务器发送的xhr请求,这是针对ajax特定的请求方式,根据ajax请求的网址分析请求的有哪些数据,在游览器开发者工具中,还可以清晰的看到ajax请求后服务器返回的json数据,javascript就是根据类似这样的数据包来重新渲染页面的。
通过游览器的开发者工具进行分析:
服务器返回的json数据:
下面是一个从微博(也存在ajax请求)页面爬取信息的爬虫例子:
from urllib.parse import urlencode
import requests
base_url = 'https://m.weibo.cn/api/container/getIndex?'
headers = {
'Host': 'm.weibo.cn',
'Referer': 'https://m.weibo.cn/u/2830678474',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36',
'X-Requested-With': 'XMLHttpRequest'
}
# 通过requests模拟ajax请求发送获取微博页面的某一分页
def get_page(page):
params = {
'type': 'uid',
'value': '2830678474',
'containerid': '1076032830678474',
'page': page
}
url = base_url + urlencode(params)
try:
response = requests.get(url, headers=headers)
if response.status_code == 200:
return response.json()
except requests.ConnectionError as e:
print('Error', e.args)
from pyquery import PyQuery as pq
# 提取返回json数据中的信息
def parse_page(json):
if json:
items = json.get('data').get('cards')
print(type(items))
for item in items:
item = item.get('mblog')
if(item):
weibo = {}
# 可以直接通过给空字典赋值来添加其内的元素
weibo['id'] = item.get('id')
weibo['text'] = pq(item.get('text')).text()
weibo['attitudes'] = item.get('attitudes_count')
weibo['comments'] = item.get('comments_count')
weibo['reposts'] = item.get('reposts_count')
yield weibo
if __name__ == '__main__':
for page in range(1, 10):
json = get_page(page)
results = parse_page(json)
for result in results:
with open('weibo.txt', 'wb') as file:
file.write(result)
# print(result)