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)