美丽汤的请求 - 小甜饼豆瓣爬虫
python3 requests + beautiful soup4
去爬“豆瓣最受欢迎的影评”,目的是爬取这些影评的作者的个人资料
1.模拟登陆
豆瓣er知道,上豆瓣时,如果是未登录状态,访问几个页面后,豆瓣会跳转到一个登录页面请求你注册/登录。所以,小爬虫需要解决这个问题才能爬到终点。
这里的解决方案就是模拟登陆。
一种是先分析这个网站登录时要POST哪些数据,然后通过POST这些数据(如用户名、密码、验证码等)实现模拟登陆。
还有一种是用自己实际登录后的cookie来模拟登陆。
这里我用的是第二种方法,推荐新手用这个。够简单粗暴~
那么怎么才能拿到小甜饼cookie呢?很简单:
1)抓包获取cookie:
我用的是chrome下的Fiddler应用(firefox下有httpfox)
首先打开Fiddler,然后去登录豆瓣。登录完回到Fiddler,去找抓到的包。
然后点击这条记录,拷贝我们的小甜饼cookie。
好一条cookie!
2)处理cookie
这时我们拷贝而来的cookie是这种格式的bid=xxxxx;gr_user=xxxxxxx;__utma=xxxxx;…
把它处理成字典就能使用啦。
1 def cookie(): 2 raw_cookies = 'blah blah' 3 cookies = {} 4 for line in raw_cookies.split(';'): 5 key, value = line.split('=', 1) 6 cookies[key] = value 7 return cookies
3)模拟登陆
给请求带上cookie就可以模拟登陆啦。(可以用只有登录才能查看的页面url来测试是否成功,比如自己看过的电影这个页面)
1 import requests 2 3 r = requests.get(url, cookies=cookies)
2.分析页面元素
通过观察页面元素,发现影评作者的个人主页地址就藏在<a class="author"></a>的href属性里。
我们用beautiful soup来解析出作者个人主页的url。
1 import requests 2 from bs4 import BeautifulSoup 3 4 r = requests.get("https://movie.douban.com/review/best/", cookies=cookies) 5 page = r.content 6 7 soup = BeautifulSoup(page, 'lxml') 8 anchors = soup.select('a.author') 9 url = a['href']
是不是很简单呢?想爬取其他信息存储起来,方法也类似。可以看看beautiful soup的文档,很简单易懂。
以及某些时候要用一些正则re,来匹配到需要的信息。比如要拿到粉丝的数量,rinka的个人主页显示xxx被666人关注。这里的“666”就要用正则拿到:
1 import re 2 3 el = soup.find("p", class_="rev-link").a 4 followers = re.findall(r'(\d+)人关注$', el.getText())
3.细节问题
比如不设置间隔时间一直请求-美丽汤-请求-美丽汤的话,很可能会得到一个又一个的403页面。
比如网站可能分页,需要观察url里的参数来循环请求-美丽汤。
4.后续
爬到的部分数据如下:
{
"昵称": "淮声",
"常居地": "保密",
"注册豆瓣时间": "2015/01/28",
"粉丝": "30",
"排名": 1
},
{
"昵称": "凌睿",
"常居地": "四川成都",
"注册豆瓣时间": "2012/08/07",
"粉丝": "4472",
"排名": 2
},
{
"昵称": "方城尉",
"常居地": "保密",
"注册豆瓣时间": "2016/12/10",
"粉丝": "0",
"排名": 3
},
{
"昵称": "萌萌哒兔子",
"常居地": "保密",
"注册豆瓣时间": "2017/01/12",
"粉丝": "10",
"排名": 4
},
可以对数据做一些可视化分析等等。
最后
贴上完整代码:
import requests from bs4 import BeautifulSoup import urllib.request import re import json import time from datetime import datetime def cookie(): raw_cookies = 'blah blah' cookies = {} for line in raw_cookies.split(';'): key, value = line.split('=', 1) cookies[key] = value return cookies def read_page(url, method="get"): ''' read the html page via the URL ''' status_code = 0 headers = {"User-Agent": 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) \ AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.95 Safari/537.36'} cookies = cookie() while status_code != 200: if method == "get": r = requests.get(url, cookies=cookies, headers=headers) elif method == "post": r = requests.post(url, cookies=cookies, headers=headers) status_code = r.status_code print(status_code) page = r.content return page def parse_person_profile(relative, info={}): ''' retrieve the information from the personal profile page ''' r = read_page(relative) soup = BeautifulSoup(r, 'lxml') # the living city of the author try: live = soup.find("div", class_="user-info").a.getText() except AttributeError: info['常居地'] = "保密" else: info['常居地'] = live print(info['常居地']) # author join douban since el = soup.find("div", class_="user-info") div = el.find("div", class_="pl") since = re.findall(r'(\d+)-(\d+)-(\d+)加入$', div.getText()) dt = datetime(year=int(since[0][0]), month=int(since[0][1]), day=int(since[0][2])) info['注册豆瓣时间'] = dt.strftime("%Y/%m/%d") print(div.getText(),info['注册豆瓣时间']) # the count of the followers el = soup.find("p", class_="rev-link").a followers = re.findall(r'(\d+)人关注$', el.getText()) print(followers) info['粉丝'] = followers[0] return info def douban_movie_popular_comment(page_url): ''' workhouse ''' r = read_page(page_url) soup = BeautifulSoup(r, 'lxml') global info anchors = soup.select('a.author') # get authors from list for index, a in enumerate(anchors): global i name = a.span.getText() print(name) p_info = {'昵称': name, '排名': i*10 + index + 1} m = a['href'] time.sleep(3) # 间隔几秒爬取一个作者,否则会由于反爬虫机制导致403 parse_person_profile(m, p_info) info.append(p_info) return info if __name__ == "__main__": i = 0 info = [] while i < 5: num = i * 20 # 观察得出 URL序号按20增加 url = 'https://movie.douban.com/review/best/?start=' + str(num) info = douban_movie_popular_comment(url) i += 1 with open("movie", "w") as f: rlt = json.dumps(info, indent=4, ensure_ascii=False) f.write(rlt)