爬虫爬取一击男吧漫画

南笙酒味 提交于 2020-02-05 02:18:07
爬虫分析:
简单流程:
Created with Raphaël 2.2.0一击男贴吧精品区url地址用xpath和re筛选目标标题保存标题名称和帖子url获取下一页的url地址循环获取所有符合的帖子标题和url遍历符合的帖子url列表分析帖子,获取所有一楼用户id所发的图片帖子下一页url获取所有图片url构造图片url,请求保存图片
分析筛选目标标题
  • 精品区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.

github源码地址

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!