最近在开始做关于百度贴吧爬虫时,遇到了xpath解析贴吧网页死活解析不出来的情况,试了火狐和Chrome浏览器都是这样,愤怒之下选择了使用BeautifulSoup框架,(百度出来的,我是个初学者),折腾了半天总算解析出来一些期望的数据,贴上一些个人遇到的坑和总结的经验:
# -*- coding: utf-8 -*-
import scrapy
from bs4 import BeautifulSoup
import re
import urllib
# import requests
# from copy import deepcopy
class TiebaSpider(scrapy.Spider):
name = 'tieba'
allowed_domains = ['tieba.com']
start_urls = ['https://tieba.baidu.com/f?kw=%C0%EE%D2%E3&fr=ala0&tpl=5']
# 这个对应的是手机版极速模式的李毅吧爬虫,电脑版的不好做,做手机版的需要改浏览器的User-Agent,
# 火狐上需要装个插件叫UserAgent Switcher即可实现修改
def parse(self, response):
# 根据帖子进行分组
# 网页的内容保存在response的body属性中,之前一直以为在response中
# response具体是什么,直接print出来即可
content = BeautifulSoup(response.body, 'lxml')
div_list = content.find_all(attrs={'class': re.compile("i.*")})
# print(div_list)
for div in div_list:
if div.a is not None:
# 正常的div数据类型都是BeautifulSoup中的tag标签,\
# 可以直接对div.a进行索引取链接的,但是有部分老鼠屎div.a是空的(可能是由于\
# 没有选取好attrs的匹配条件),因此 进行一个非空判断,否则在运行时就会报错:\
# TypeError: 'NoneType' object is not subscriptable
item = {}
# 打印出div.a的类型,发现其中有部分数据是None类型的
# print(type(div.a))
item["href"] = div.a["href"]
# urllib.parse.urljoin()函数后还能够自动根据response的url补全对应的url地址
item["href"] = urllib.parse.urljoin(response.url, item["href"]) # 取链接地址
item["title"] = div.a.string # 取标签对应的内容,即帖子的名字
item["img_href"] = [] # 整个空列表,无论之后是第几页的图片链接,都直接使用
# extend()函数进行追加,如此就可以防止前一页的图片链接数据被后一页的图片链接所覆盖
yield scrapy.Request(
item["href"],
callback=self. parse_detail, # 注意此处是函数名,没有加括号,加了括号就成了函数调用
meta={"item": item},
dont_filter=True # 此处必须设置dont_filter,否则这些贴吧页面对应的请求会被过滤掉
# 怎么查看是被过滤掉:我在下面的parse_detail()函数中打印response没有任何结果,参照下面
# 的链接查出来的:https://blog.csdn.net/mr_hui_/article/details/80435941
# 至于为什么被过滤掉,我就是个菜鸡我也不知道
)
# 列表页翻页
content = BeautifulSoup(response.body, 'lxml')
if content.find(text="下一页") is not None:
next_url_tag = content.find(text="下一页").parent
next_url = next_url_tag["href"]
next_url = urllib.parse.urljoin(response.url, next_url) # 补全下一页的URL地址
# print(next_url)
yield scrapy.Request(
next_url,
callback=self.parse,
dont_filter=True
)
# 处理帖子详情页中的图片
def parse_detail(self, response):
item = response.meta["item"]
# if response is not None:
# print(response)
# else:
# print("空响应")
content2 = BeautifulSoup(response.body, 'lxml')
image_list = content2.find_all(href=re.compile("http://.+\.jpg"))
for imge_src in image_list:
# print(imge_src["href"])
# 防止下一页的图片链接覆盖掉上一页图片的链接,采用追加
# print(imge_src["href"])
item["img_href"].extend(imge_src["href"])
with open("./imag_link.txt", "a+") as file:
file.write(imge_src["href"] + "\n")
if content2.find(text="下一页") is not None: # 注意非空判断,否则又会报错
next_url_tag = content2.find(text="下一页").parent # 根据文本内容为下一页找对应的标签
next_url = next_url_tag["href"]
next_url = urllib.parse.urljoin(response.url, next_url) # 补全下一页的URL地址
if next_url is not None:
yield scrapy.Request(
next_url,
callback=self.parse_detail,
meta={"item": item},
dont_filter=True
)
上述代码只能爬取出来一部分内容,无法完全爬出来,我也不知道哪里出问题了(尴尬),对应的项目中setting.py我只是做了如下修改:
LOG_LEVEL = "WARNING"
USER_AGENT = "Mozilla/5.0 (Android 7.0; Mobile; rv:61.0) Gecko/61.0 Firefox/61.0"
# Obey robots.txt rules
ROBOTSTXT_OBEY = False
找不出来bug很难受,哪位大佬若发现了,请多指教,在此先谢过
BeautifulSoup框架教程地址:https://www.crummy.com/software/BeautifulSoup/bs4/doc.zh/