爬虫分析:
简单流程:
分析筛选目标标题
- 精品区url地址:https://tieba.baidu.com/f?kw=%E4%B8%80%E5%87%BB%E7%94%B7&ie=utf-8&tab=good&cid=1&pn=0
- 参数kw=“贴吧名字”,这里是“一击男”。
- tab=good 是精品区的意思,不用管。
- cid=1 是精品区下的帖子分类,因为要爬取的是精品区的帖子,所以这里选cid=1,“村田重置”
- 浏览器进入开发者模式,看看代码。Ctrl+f搜索一下某个标题,“二人”,因为要用xpath和re,所以要。找一下一下标题代码的规律。
<a rel="noreferrer" href="/p/6366353276" title="164话嵌字汉化【二人小组】" target="_blank" class="j_th_tit ">164话嵌字汉化【二人小组】</a>
- 因为百度返回的html都在注释里面对使用xpath十分不友好,所以尝试使用re正则表达式直接弄出来。
- 使用正则表达式:
re_str = r'href="/p/(\d*)" title="(.*?)"'
- 可以得到形如这样的列表:
[('6459196128', '【团子汉化组】一击男重制版 168话'), ('6459257409', '168话嵌字汉化【二人小组】'), ('6459200478', '一击男168话【野生汉化菌】'),......,....]
- 元组内第一个数字可以使用字符串拼接成帖子的url。
- 元组第二个元素着是这个帖子的标题,可以再次使用正则判断筛选出我们想要的帖子。
获取下一页的url地址
- 看了一下还是不能直接用xpath,同理还是使用正则表达式。
- 使用正则表达式:
index_netx_page_re_str = r'<a href="(.*?)" class="next pagination-item " >'
(感觉正则还挺好用)可以获得下一页的url地址 - 当到了尾页的时候,正则返回的结果为空列表,所以,可以一次为条件结束循环。
分析帖子,获取帖子内容。
- 帖子的url还有一点小细节没有处理,不过问题不大,开始分析帖子内容。
- 先把其中一个帖子的html下载下来看看。
- 获取帖子内容的过程有点困难,不过,先用
pic_url = pic_html.xpath(r"//div[@class='left_section']/div[@id='j_p_postlist']/div/@data-field")
- 获取了帖子里面每一楼的文件内容,是个字典
- 图片ID就在pic_url[i][content][content]里面,这里还要用json解释一下才行
- 再对这里面的字符串使用一次正则表达式
str1 = json.loads(pic_url[15])["content"]["content"]
print(re.findall(r'src="(.*?)"', str1))
- 这样就得到了图片的地址啦
['http://tiebapic.baidu.com/forum/w%3D580/sign=1a140bfedeea15ce41eee00186003a25/77a7ec025aafa40f52d4aba6bc64034f78f01915.jpg']
获取帖子的下一页
- 这里可以使用xpath也可以使用re,看了一下好像xpath好像更好一点。那就使用xpath吧
next_pic_num = pic_html.xpath("//a[text()='下一页']/@href")
- 这里获得了2个url地址,不过都是一样的,到时候用其中一个就可以。
修修补补
- 上面获取到的url基本都需要自己使用字符串的拼接才能传入request模块去使用。
- 先爬取一个帖子的图片试试看
- 再爬取贴吧一页的贴子
- 最后再爬取整个精品区的帖子
- 几个循坏还需要理一理
- 因为要爬取的数量可能比较大,考虑使用多线程进行爬取
- 为了防止ip被封,可能还要加点延时,还是说使用代理ip,这个还要想一想
- 因为贴吧好像不用登陆也能看,就不用带cookies了
下面开始写代码
- 获取精品区帖子的url地址和标题和下一页的url地址
- 请求帖子url地址,获取响应
- 从响应中获取图片url地址和下一页url地址
- 保存图片
- 帖子的下一页内容
- 精品区的下一页内容
- 确定好了流程就先写个框架,再慢慢补充细节
class OnePunchManSpider:
def __init__(self):
self.headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/79.0.3945.88 Safari/537.36 "
}
self.boutique_area_url = "https://tieba.baidu.com/f?kw=%E4%B8%80%E5%87%BB%E7%94%B7&ie=utf-8&tab=good&cid=1&pn={}"
self.post_url = "https://tieba.baidu.com/p/{}"
def get_boutique_post_url(self):
pass
def get_response(self):
pass
def get_pic_response(self):
pass
def save_pic(self):
pass
def run(self):
# 1. 获取精品区帖子的url地址和标题和下一页的url地址
boutique_area_url, post_title = self.get_boutique_post_url()
# 2. 请求帖子url地址,获取响应
post_response = self.get_response()
# 3. 从响应中获取图片url地址和下一页url地址
pic_url, next_pic_page_url = self.get_pic_response()
# 4. 保存图片
self.save_pic()
# 5. 帖子的下一页内容
# 6. 精品区的下一页内容
if __name__ == '__main__':
开始按照思路吧功能函数一个个补全。然后加一点细节,写出了一个初步能实现功能的程序。
# -*-coding:utf-8-*-
import requests
from lxml import etree
import re
import json
import os
class OnePunchManSpider:
def __init__(self):
self.headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/79.0.3945.88 Safari/537.36 "
}
self.boutique_area_url = "https://tieba.baidu.com/f?kw=%E4%B8%80%E5%87%BB%E7%94%B7&ie=utf-8&tab=good&cid=1&pn=0"
self.post_url = "https://tieba.baidu.com"
self.re_post_url = re.compile(r'href="(/p/\d*)" title="(.*?)"') # 获取帖子url的正则
self.re_pic_url = re.compile(r'src="(.*?)"')
self.re_boutique_post_next_page_num = re.compile(r'<a href="(.*?)" class="next pagination-item "')
def get_boutique_post_url(self, response_str):
post_list = self.re_post_url.findall(response_str)
return post_list
def get_pic_url(self, pic_response_str):
response_str_html = etree.HTML(pic_response_str)
post_data = response_str_html.xpath("//div[@id='j_p_postlist']/div/@data-field")
pic_num_list = []
louzhu_id = json.loads(post_data[0])["author"]["user_id"]
for i in post_data:
user_id = json.loads(i)["author"]["user_id"]
if user_id == louzhu_id:
str1 = json.loads(i)["content"]["content"]
pic_num_list += re.findall(r'src="(.*?.jpg)"', str1)
return pic_num_list
def get_next_page_num(self, response_str):
url = self.re_boutique_post_next_page_num.findall(response_str)
if url:
return url[0]
def get_next_pic_page_num(self, response_str):
pic_html = etree.HTML(response_str)
next_pic_page_url = pic_html.xpath("//a[text()='下一页']/@href")
if next_pic_page_url:
return self.post_url + next_pic_page_url[0]
def save_pic(self, pic_url_list, path):
for i in pic_url_list:
pic_name = path + '/' + str(pic_url_list.index(i))
pic_response = requests.get(i, headers=self.headers)
with open(pic_name, "wb") as f:
f.write(pic_response.content)
def analyze_post_list(self, post_list):
need_post_list = []
for i in post_list:
if re.search('二人小组', i[1]):
need_post_list.append(i)
elif re.search('不良漢化', i[1]):
need_post_list.append(i)
elif re.search('超市特卖', i[1]):
need_post_list.append(i)
elif re.search('个人汉化', i[1]):
need_post_list.append(i)
return need_post_list
def get_post_url(self, need_post_list):
return {i[1]: self.post_url + i[0] + '?see_lz=1' for i in need_post_list}
def get_pic_url_list(self, post_url):
pic_url_list = []
while post_url:
pic_response = requests.get(post_url, headers=self.headers)
pic_response_str = pic_response.content.decode()
pic_url_list += self.get_pic_url(pic_response_str)
post_url = self.get_next_pic_page_num(pic_response_str)
return pic_url_list
def mkdir(self, path):
# 去除首位空格
path = path.strip()
# 去除尾部 \ 符号
path = path.rstrip("\\")
# 判断路径是否存在
# 存在 True
# 不存在 False
isExists = os.path.exists(path)
# 判断结果
if not isExists:
# 如果不存在则创建目录
# 创建目录操作函数
os.makedirs(path)
print
path + ' 创建成功'
return True
else:
# 如果目录存在则不创建,并提示目录已存在
print
path + ' 目录已存在'
return False
def run(self):
# 1. 获取精品区帖子的url地址和标题
boutique_area_page_url = self.boutique_area_url
url = 1
while url:
response = requests.get(boutique_area_page_url, headers=self.headers)
response_str = response.content.decode()
post_list = self.get_boutique_post_url(response_str) # 获取每一页的帖子的标题和url地址
need_post_list = self.analyze_post_list(post_list) # 简陋的筛选出想要的帖子
post_url_dict = self.get_post_url(need_post_list) # 加工成{“标题”:“url”}形式的字典
# 遍历字典,请求帖子获取帖子内容
for i in post_url_dict:
print(i)
save_path = "./onepunchman/" + i
if not os.path.exists(save_path): # 判断目录是否存在
self.mkdir(save_path) # 创建目录
pic_url_list = self.get_pic_url_list(post_url_dict[i]) # 获取图片url列表
print(len(pic_url_list))
print(pic_url_list)
self.save_pic(pic_url_list, save_path) # 保存图片url列表
url = self.get_next_page_num(response_str) # 获取下一页url地址
if url
boutique_area_page_url = 'https:' + url
else:
break
if __name__ == '__main__':
onepunchmanspider = OnePunchManSpider()
onepunchmanspider.run()
程序说明
- 爬取一击男贴吧所有漫画内容
- 创建文件夹并保存到本地
- 如果本地已经存在就执行下一话
后续改进
- 添加多线程加快爬虫速度
- 添加ip池防止爬取过快ip被封
- 程序还可以修改得更好
- 程序里面字典列表字符串绕来绕去
- 功能的模块化应该可以分得更精确
写正则的时候在浏览器多点几下比啥都好,这次点了一下【只看楼主】,省了不少脑细胞。
Anyway, done is better than perfect.
来源:CSDN
作者:W.zd
链接:https://blog.csdn.net/w_zd1024/article/details/104131529