作业要求:
增加抓取百度贴吧中,各楼层的回复评论及帖子内部翻页功能
正文内容:
1、功能分析
(1)如何获取回复评论
(2)如何获取帖子下一页地址
2、实现中的问题
(1)Windows下MySQLdb问题
(2)内容包括emoji导致数据库存储问题
3、代码实现
功能分析
(1)如何获取回复评论
首先打印 response 的内容,找到如下图红框部分,判断应该默认是收起回复,而我们通过浏览器访问时,额外发送了请求异步加载回复。
因此,下图通过 Chrome 的 f12 开发者模式刷新查看网络请求,选择 XHR 进行筛选,观察这些左侧窗口中的 URL,判断 totalComments 这个可能是,查看 Response 内容后更加肯定,这是一个 JSON 字符串,通过在线网站格式化之后会清楚很多。
https://tieba.baidu.com/p/totalComment?t=1568122669553&tid=6173554079&fid=4293157&pn=1&see_lz=0
上面就是获取回复的 URL 地址格式,参数包括:时间戳、帖子ID(thread_id)、贴吧ID(forum_id)、页码、是否看楼主(?)。帖子ID获取方式多种,我是通过帖子 URL 正则匹配得到,见后文代码。贴吧ID这里固定就行。
如果查看下一页回复的 URL,还可以找到下面的 URL 格式,这是获取单一评论的回复。
https://tieba.baidu.com/p/comment?tid=6173554079&pid=126257021241&pn=1&t=1568123683835
(2)如何获取帖子下一页地址
帖子的下一页地址不同于列表的下一页地址,不同直接通过 next 的样式得到,不过也简单,找到所有 pb_list_pager 样式下的 a 标签,判断 text 为“下一页”即可。
实现中的问题
(1)Windows下MySQLdb问题
我是 Windows 操作系统,使用 PyCharm 作为 IDE,安装 MySQLdb 一直是失败,最终选择 mysql-connector 作为数据库连接。因此代码区别于大周老师略有修改。
dbpool = adbapi.ConnectionPool("mysql.connector", **dbparms)
(2)内容包括emoji导致数据库存储问题
这个问题的解决参看 如何转义emoji表情,让它可以存入utf8的数据库?,文章下面的第一个答案是最简洁的方法。这里可以了解更多emoji。
代码实现
需要编写代码的有三个文件:spiders/baidu.py、items.py、pipelines.py,其中主要是改动 baidu.py 中的代码,故下面仅贴出此部分代码。
# -*- coding: utf-8 -*-
import json
import re
import time
from urllib import parse
import requests
import scrapy
from homework6.tieba_spider.items import TiebaItem
class BaiduSpider(scrapy.Spider):
name = 'baidu'
allowed_domains = ['tieba.baidu.com']
start_urls = ['https://tieba.baidu.com/f?ie=utf-8&kw=%E9%98%B2%E8%AF%88%E9%AA%97']
def __init__(self):
self.count = 0
def parse(self, response):
# 获取帖子中的url
p_url_list = response.css('a.j_th_tit::attr(href)').getall()
# 挨个的url进行访问
for p_url in p_url_list:
yield scrapy.Request(parse.urljoin(response.url, p_url), callback=self.parse_detail)
# 避免无限下去,只处理前10页
if self.count < 10:
self.count += 1
# 定位到下一页,并翻页
next_url = 'https:' + response.css('a.next::attr(href)').get()
yield scrapy.Request(next_url, callback=self.parse)
def parse_detail(self, response):
tid = re.search('d+', response.url).group()
title = response.css('h3::text').get()
# 获取回复
comment_list = self.get_comments(tid)['data']['comment_list']
if comment_list:
comment_list_keys = comment_list.keys()
else:
comment_list_keys = {}
# 获取评论各部分信息
div_list = response.css('div.l_post')
for div in div_list:
tieba_item = TiebaItem()
tieba_item['title'] = title
tieba_item['author'] = div.css('a.p_author_name::text').get()
tieba_item['content'] = div.css('div.d_post_content::text').get().strip()
tieba_item['floor'] = div.css('span.tail-info::text').getall()[-2]
tieba_item['reply_time'] = div.css('span.tail-info::text').getall()[-1]
# 从所有回复中找到该评论下的回复
content_id = re.search('d+', div.css('div.d_post_content::attr(id)').get()).group()
if content_id in comment_list_keys:
comments = []
for comment in comment_list[content_id]['comment_info']:
comments.append(comment['content'])
tieba_item['comments'] = str(comments)
else:
tieba_item['comments'] = ''
yield tieba_item
# 得到下一页帖子
page_links = response.css('li.pb_list_pager a')
if page_links:
for page_link in page_links:
if page_link.css('::text').extract()[0] == '下一页':
next_p_url = page_link.css('::attr(href)').extract()[0]
yield scrapy.Request(parse.urljoin(response.url, next_p_url), callback=self.parse_detail)
break
@staticmethod
def get_comments(tid):
request_url = 'https://tieba.baidu.com/p/totalComment?t=' + str(int(time.time())) +
'&tid=' + tid +
'&fid=4293157&pn=1&see_lz=0'
headers = {
'user-agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36'
}
proxies = {
'url': 'http://163.204.245.89:9999'
}
response = requests.get(request_url, headers=headers, proxies=proxies)
response_json = json.loads(response.text)
return response_json
运行结果:
总结:
1、使用框架 Scrapy 比直接使用 requests 方便、强大,但也更复杂,比如 Scrapy 采用异步方式请求令我不知如何将请求到的 comments 给 tieba_item,而最终只能使用 requests 方式。
2、其实发现 emoji 导致数据库存储问题花了一些时间的,原来根据报错信息只以为是数据库默认采用 latin 编码而不是 utf8 导致,下次将能够更快发现问题。
参考链接:
1、《Scrapy 1.7 官方中文文档》(https://www.osgeo.cn/scrapy/index.html)
2、《emoji(绘文字)》(https://www.qqxiuzi.cn/zh/emoji.html)
3、《如何转义emoji表情,让它可以存入utf8的数据库?》(https://segmentfault.com/q/1010000003711491/a-1020000003712160)