python(数据分析与可视化)四
动态网站的爬取
今天我们来讲解一下有关js动态加载后的数据爬取
1.京东评论初步尝试
import requests
from lxml import etree
#单个商品详情页url
url = 'https://item.jd.com/100009077475.html'
headers = {
#没有user-agent 返回简短的html代码,js重定向到首页
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36'
}
#请求图集页
resp = requests.get(url,headers=headers)
if resp.status_code == 200:
html = resp.text
print(html) #得到一些html源代码,为具体表现
dom = etree.HTML(html)
pattern = '//div[@class ="comment-item"]//p[@ class ="comment-con"]/text()'
comments = dom.xpath(pattern)
print(len(comments),comments)
经过类似于上一博客讲解的静态网页数据的爬取,我们会发现 xpath可以在开发者工具elements中匹配11条评论,但是代码中匹配到的确实空列表,这就是涉及到了跨域,下面我们一步一步来进行分析。
2.动态网站和静态网站分析
1.静态网站 :
几乎所有的后端语言的web框架,java ssh、php、python flask django.服务端web程序接收到请求后,从数据库取数据,把数据拼凑到事先准备的html模板骨架中,形成一个完整的网页html,最终响应,浏览器解析,用户看到效果。这种技术非常流行,广义上来说是动态网站,狭义上来说响应前html已经确定好了。
缺点:如果页面已经变更,需要刷新页面才能看到,需要手动频繁刷新页面,不刷新就可能错失信息,刷新又太累。
2.js动态网站:
第一次请求从后台收到html源代码,html源代码只有网页菜单导航等公共部分和js,浏览器拿到第一次响应,解析js,js中发起后续异步后台请求,从后台拿到纯数据,通过js把数据渲染某一处div里面,最终用户看到完整网页。
优点:传输更快,首屏加载更快,局部更新,体验更好
场景:评论、微博 朋友圈提示、注册表单再提交前提示用户已注册
两者间的分别:
第一次url document请求得到的源代码,同网页最终(开发者工具elements)比对:
1.如果一致,静态网站
2.整个网页刷新 -> 静态 局部刷新->动态
3.xpath是否能取到目标数据,如果娶不到,则数据在后续的js请求里
3.评论请求寻找分析
- 找js xhr请求评论数据,不关心对方js怎么写和渲染到网页,只要找到http请求,然后用python模拟
寻找技巧
1.开发者工具network,过滤xhr、js请求
2.找post类型的请求
3.找比较像的名字 comment api get_info get+list
4.响应的是纯数据,而不是html信息
5.尝试在网页中触发异步js数据请求,在开发者工具中看有没有新增请求
6.network工具获取焦点,ctrl+f 打开隐藏的搜索框,在所有请求、请求头,响应全文搜索关键字
4.json格式
为什么用json,xml,而不用list ,dict等,这是因为不同计算机互相交流数据,每种计算机用的编程语言不同,java,php,python,C,C++,Go
类似的结构,因为解释器底层实现不一样,表现同样数据的二进制不一样,每一台计算机上的编程语言和服务不一样,需求‘中间格式’,所以,有统一格式,XML和Json,【字符串】
- json可以做数据传输文件
XML 过去很流行,但很麻烦,现在用的少
Json格式 字符串
json格式语法: {}内键值对属性和值,[]数组一组,基本类型 整型,字符串。字符串用【双引号】
json里只能使用双引号
下面是一个简单的举例:
import json
xiaoming_json_str = """
{
"name": "小明",
"age": 54,
"parent": [
{
"name": "小明爸爸"
},
{
"name": "小明妈妈"
}
]
}
"""
#打印在控制台的结果看不出变量是字符串还是字典,应该用type()函数判读
print(type(xiaoming_json_str),xiaoming_json_str)
#json字符串 -> python内置数据结构
xiaoming_dict = json.loads(xiaoming_json_str)
#json.losd(本地文件.json)
print(type(xiaoming_dict),xiaoming_dict)
print(xiaoming_dict['name'])
for parent in xiaoming_dict['parent']:
print(parent['name'])
#python内置数据结构 -> json 字符串
students = [
{'name': "小红", 'age': 14, 'gender': '男'},
{'name': "小明", "age": 15, "gender": "男"},
{'name': "小明", "age": 11, "gender": "女"}
]
students_json_str = json.dumps(students)
print(type(students_json_str), students_json_str)
5.请求京东评论接口
接下来就是一个比较完整的京东评论接口的爬取实例了:
import json
import requests
base_url = 'https://club.jd.com/comment/productPageComments.action'
#本次请求头只用伪造users-agent即可,但前段时间测试需要cookie字段
headers = {
# 'Cookie': '__jdv=76161171|direct|-|none|-|1610928737206; __jdu=1610928737155940443424;',
# 'Referer': 'https://item.jd.com/',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36'
}
#tips:从开发者工具network请求头下面的query params复制下来再调整,使用编辑器列编辑模式 alt+shif+鼠标拖动
params = {
#'callback': 'fetchJSON_comment98', #特殊的 jsonp->json
'productId': 100009077475, #商品id
'score': 0,
'sortType': 5,
'page': 1, #第几页
'pageSize': 10,
'isShadowSku': 0,
'rid': 0,
'fold': 1
}
resp = requests.get(base_url,headers=headers,params=params)
status_code = resp.status_code
comments_json = resp.text
print(comments_json)
comments_obj = json.loads(comments_json)
print(comments_json)
comments = comments_obj['comments']
for c in comments:
#print(c)
cid = c['id']
content = c['content']
creation_time = c['creationTime']
images = c['images']
product_color = c['productColor']
print('-'*100)
print(cid,content)
其中需要注意的是,京东评论接口返回得到的是jsonp 格式,涉及跨域问题,需要先将jsonp转为json
方法一:python字符串方法删除固定长度无用字符串
方法二:(推荐)上网找从jsonp过滤json正则
方法三:本例中发现修改参数可以直接访问json
今天的分享就到这里,如果还想了解更多,可以看我主页!