准备流程
- 确认网页是否为动态数据
右键查看网页源代码中搜你要的数据中的关键词,若搜不出来,就说明是动态数据 - 只要是动态数据,直接去抓包
控制台Network ->xhr -> 点击左下角各个数据包 ->preview,找到你想要的数据所在的那些数据包后 -> Headers -> general ->request-URL:找到后端返给前端的api
电影总数的request-URL为:https://movie.douban.com/j/chart/top_list_count?type=13&interval_id=100%3A90 - 分析headers中查询参数query string params的规律
比对各个数据包的查询字符串的不同点,看哪些参数是变化的:
type: 13
interval_id: 100:90
action:
start: 0
limit: 20
type: 13
interval_id: 100:90
发现start和type是变化的,start表示第几页,从0开始以20递增,type表示电影类型(爱情等),与类型的关系可从首页的html节点中抓取出来 - 在地址栏中请求我们抓到的后端传给前端的api包,查看json数据的具体格式
后端返回给前端的api,返回的是json串,发现此处数据格式为:’[{},{},{}]’
思路
要实现抓取所有电影分类的数据,需要知道每个分类下电影部数的总数,发现这个数据在电影类别页面下也有,而且也是动态数据,也需要抓包获取
详细代码
import requests
import time
import random
from fake_useragent import UserAgent
import json
import re
class DoubanSpider:
def __init__(self):
#各分类排行的电影数据json包的api
self.url = 'https://movie.douban.com/j/chart/top_list?type={}&interval_id=100%3A90&action=&start={}&limit=20'
#各分类排行电影的电影总数Json包api
self.url_1 = 'https://movie.douban.com/j/chart/top_list_count?type={}&interval_id=100%3A90'
#1. 发请求获取响应的方法
def get_html(self,url):
headers = {'User-Agent':UserAgent().random}
html = requests.get(url=url,headers=headers).text
return html
#2. 解析提取电影数据(动态)的方法
#动态数据的提取不使用re和xpath,而是分析json包(因为前端使用ajax向后端要数据,后端返给前端的数据是json串)
def parse_html(self,url):
#拿到的响应内容是Json格式的字符串:'[{},{},{}]' html = self.get_html(url)
#从Json串中取出你想要的数据:电影名,得分,年代,并存放到字典中 #将json串转换成python的数据类型:[{title:xxx},{score:xxx},{}]
html = self.get_html(url)
#将json格式的字符串转为Python数据类型
html = json.loads(html)
item = {}
#对字典进行遍历赋值,每次遍历赋值都会覆盖修改之前的数据打印出来查看
for film in html:
item['name'] = film['title']
item['score'] =film['score']
item['time'] = film['release_date']
print(item)
#为了拼接url,需要电影类型码这个变量,该方法用于获取电影类型码,发现是数据在静态页面中,使用re解析
def get_types_dict(self):
#类型码数据所在网页
url = 'https://movie.douban.com/chart'
#请求改页面获取响应
html = self.get_html(url)
#在页面源代码中找type值和电影类型,使用re解析(方便),源码中的标签如下:
#<span><a href="/typerank?type_name=剧情&type=11&interval_id=100:90&action=">剧情</a></span>
#写正则,返回的数据格式为:[('type_name','type'),('剧情','11'),(),....]
#<span><a href="/typerank.*?type_name=(.*?)&type=(.*?)&interval_id=100:90&action=">.*?</span>
p = re.compile('<span><a href="/typerank.*?type_name=(.*?)&type=(.*?)&interval_id=100:90&action=">.*?</span>',re.S)
r_list = p.findall(html)
#将拿到的数据格式:[(),(),...]做成字典
types_dict = {}
for r in r_list:
types_dict[r[0]] = r[1]
return types_dict
#2. 请求并解析提取各分类排行电影总部数的方法
def get_total(self,type):
#请求json包并获取响应
html = self.get_html(self.url_1.format(type))
#将json串:{key1:value1,....} 转为python数据类型:字典
html = json.loads(html)
#提取电影总量数据
total = html['total']
return total
#启动函数:需要给出请求的url
def run(self):
#获取电影类型对应类型码的数据,数据格式为字典:{类型1:类型码1,类型2:类型2,。。。。}
types_dict = self.get_types_dict()
#将所有电影类型输出出来供选择
menu ={}
for key in types_dict:
menu = menu + key + ' | '
print(menu)
type = input('请输入用户类型:')
#获取用户输入类型的type码
type = types_dict[type]
total = self.get_total(type)
for start in range(0,total,20): #第二个参数是想要遍历的总电影数
#拼接电影数据的json包的url
url = self.url.format(type,start)
self.parse_html(url)
time.sleep(random.uniform(0,1))
if __name__ == '__main__':
spider = DoubanSpider()
spider.run()