NanaseHaruka

python脚本爬取王者荣耀吧

本篇博客介绍了这次由于项目需求,爬取百度贴吧--王者荣耀吧的帖子的过程。


一、安装第三方库

pip install requests
pip install bs4
pip install lxml
pip install html5lib

二、源码分析

1. 分析请求链接的规律

  F12打开开发者工具,百度搜索王者荣耀吧,在Network选项卡找到对应请求,可以看到其请求链接基本遵循如下规律:

https://tieba.baidu.com/f?kw=%E7%8E%8B%E8%80%85%E8%8D%A3%E8%80%80&ie=utf-8&tab=corearea&pn=450
kw: 贴吧名字(王者荣耀)
ie: 编码方式
tab: 首页标签
pn: 页码

其中首页标签的选项卡如下,可以一个一个点一遍试试,看看对应URL的tab字段是什么值:

 

2. 分析Response

  找到该请求的响应,可以看到每一条帖子的概要内容都在,没有用AJAX,故无需分析XHR。

  但是需要注意的是,我们需要爬取的内容是每一条帖子的信息和内容,这一部分没有包含在<html>...</html>标签内,而是在之后另外用<code>......</code>包裹,并且,是注释内容<!-- .... -->。因此,在用beautifulsoup解析的时候,如果不做处理,是无法解析出我们想要的内容的。

3. 正式开始爬取

  需求:爬取王者荣耀吧的帖子并保存到本地,可以选择页数,选择标签,选择指定日期之前的帖子,选择包含关键词的帖子。每一条帖子包含标题、链接、发表日期,详细内容,所有回帖。

(1)发送请求,获取响应

 1 def get_html(post_name, tab, pn):
 2     """
 3     获取html
 4     :param post_name: 贴吧名
 5     :param tab: 标签名
 6     :param pn: 页码
 7     :return:
 8     """
 9     try:
10         url = \'https://tieba.baidu.com/f\'
11 
12         headers = {
13             \'User-Agent\': \'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) \
14                           Chrome/75.0.3770.100 Safari/537.36\'
15         }
16         # tag:
17         # 核心区:corearea; 看帖:main
18         data = {
19             \'kw\': post_name,
20             \'tab\': tab,
21             \'pn\': pn,
22         }
23         response = requests.get(url, params=data, headers=headers, timeout=30)
24         # 必须修改HTML页面,把HTML结束标签改到最后,否则soup解析只到原来的HTML标签就结束了,后面的code标签里的内容被丢弃
25         html = response.text.replace(\'</body>\', \'\')
26         html = html.replace(\'</html>\', \'\')
27         response = html + \'</body></html>\'
28         # response.encoding = \'utf-8\'
29         # print(response.text)
30         return response
31     except RuntimeError:
32         return \'ERROR\'

注意代码中的注释,根据之前的分析,我们要的内容都不在<html><body>...</body></html>标签包裹之内,后面soup无法解析到,所以要修改获得的源码。

(2)解析响应

 1 def get_post_info(html, m, pn):
 2     """
 3     获取帖子的标题、链接信息,并从中筛选出有特定关键词的帖子
 4     :param html: 处理后的HTML页面
 5     :param m: month
 6     :param pn: 页码
 7     :return: 帖子信息
 8     """
 9     url = \'https://tieba.baidu.com\'
10     soup = BeautifulSoup(html, \'lxml\')
11     # 找到目标code标签,返回tag列表
12     code = soup.find_all(\'code\', attrs={\'id\': \'pagelet_html_frs-list/pagelet/thread_list\'})
13     # 提取code标签的内容(注释),返回列表
14     comment = code[0].contents
15     # print(type(comment[0]))
16     # comment = code[0].string
17     # print(type(comment))
18     # 重新开始解析comment
19     soup = BeautifulSoup(comment[0], \'lxml\')
20     # soup = BeautifulSoup(comment, \'lxml\')
21 
22     # 找到目标li标签
23     info = []
24 
25     # # 先找到置顶帖
26     # litags_top = soup.find_all(\'li\', attrs={\'class\': \'j_thread_list thread_top j_thread_list clearfix\'})
27     # for li in litags_top:
28     #     info_top = dict()
29     #     try:
30     #         info_top[\'title\'] = li.find(\'a\', attrs={\'class\': \'j_th_tit\'}).text.strip()
31     #         info_top[\'link\'] = \'\'.join([url, li.find(\'a\', attrs={\'class\': \'j_th_tit\'})[\'href\']])
32     #         info_top[\'time\'] = li.find(\'span\', attrs={\'class\': \'pull-right is_show_create_time\'}).text.strip()
33     #         info.append(info_top)
34     #     except:
35     #         print("错误:获取置顶帖标题失败!")
36 
37     # 再找到常规帖,提取标题、链接、发表日期、摘要信息
38     litags = soup.find_all(\'li\', attrs={\'class\': \'j_thread_list clearfix\'})
39     for li in litags:
40         try:
41             info_norm = dict()
42             info_norm[\'title\'] = li.find(\'a\', attrs={\'class\': \'j_th_tit\'}).text.strip()
43             info_norm[\'link\'] = \'\'.join([url, li.find(\'a\', attrs={\'class\': \'j_th_tit\'})[\'href\']])
44             info_norm[\'date\'] = li.find(\'span\', attrs={\'class\': \'pull-right is_show_create_time\'}).text.strip()
45             info_norm[\'abstract\'] = li.find(\'div\', attrs={\'class\': \'threadlist_abs threadlist_abs_onlyline\'}). \
46                 text.strip()
47             info.append(info_norm)
48         except AttributeError as e:
49             print("错误:%s,可能是因为没有找到相应的标签" % e.args)
50         except:
51             print("错误:获取常规帖标题及摘要失败!")
52 
53     print(\'第 %s 页已经爬取成功, 开始处理...\' % (pn/50+1))
54     # 筛选发表日期在一个月以内,且标题和摘要里有关键词[\'发热\',\'卡\', \'掉帧\', \'\']的帖子
55     # 获取当日日期
56     today = time.strftime(\'%m-%d\', time.localtime(time.time()))
57     month = int(today.split(\'-\')[0])
58     day = int(today.split(\'-\')[1])
59 
60     if month - m >= 1:
61         last_month = month - m
62     else:
63         last_month = 12 + (month - m)
64     # if last_month == 2 and day >= 29:
65     #     one_month_before = \'\'.join([str(last_month), \'-\', \'28\'])
66     # else:
67     #     one_month_before = \'\'.join([str(last_month), \'-\', str(day)])
68 
69     # num = len(info)
70     info_new = []
71     for post in info:
72         if \':\' in post[\'date\']:
73             info_new.append(post)
74         elif int(post[\'date\'].split(\'-\')[0]) == last_month and int(post[\'date\'].split(\'-\')[1]) >= day:
75             info_new.append(post)
76         elif int(post[\'date\'].split(\'-\')[0]) == month and int(post[\'date\'].split(\'-\')[1]) <= day:
77             info_new.append(post)
78 
79     # # 关键词分开存放
80     # keywords = [\'发热\', \'卡顿\', \'掉帧\', \'卡死\']
81     # num = len(keywords)
82     # info_has_kw = [[] for i in range(num)]
83     # for post in info_new:
84     #     for i in range(num):
85     #         if keywords[i] in post[\'abstract\']:
86     #             info_has_kw[i].append(post)
87     #             break
88 
89     print(\'第 %s 页已经处理完成,开始爬取下一页...\' % (pn/50+1))
90     # return info_has_kw
91     return info_new

 

(3)保存到本地

def save2file(info, savepath=os.path.dirname(os.path.realpath(__file__))+\'\\post.txt\'):
    """
    将爬取到的帖子内容写入到本地,保存到指定目录的txt文件中,保存目录默认为当前目录。
    :param info: 帖子内容
    :param savepath: 输出文件路径,默认为当前目录
    :return:
    """
    # num = len(info)
    # for i in range(num):
    #     with open(savepath, \'a+\') as f:
    #         for post in info[i]:
    #             f.write(\'标题:{} \t 链接:{} \t\'.format(post[\'title\'], post[\'link\']))
    with open(savepath, \'a+\') as f:
        for post in info:
            f.write(\'标题:{} \t 链接:{} \t\'.format(post[\'title\'], post[\'link\']))
    print("当前页面已经保存到本地!")

 (4)主程序

if __name__ == \'__main__\':
    post_name = \'王者荣耀\'
    tab = \'main\'
    # 循环控制爬取的页数
    for pn in range(10):
        html = get_html(post_name, tab, pn*50)
        info = get_post_info(html, 3, pn*50)
        # print(info)
        save2file(info)
    print(\'-------所有帖子下载完成-------\')

 

分类:

技术点:

相关文章: