实现简书数据爬取

作者: Day_Winston | 来源:发表于2018-08-01 11:14 被阅读36次

能够爬取简书大部分数据,后续迭代较为方便

from os.path import sep, altsep
import requests
import json
import os
import re


class SimpleBook(object):
    """简书爬取,分类主题,主题下的单篇文章和作者还是有更新时间
    # 模块分类
    1.初始化方法
    2.处理响应请求返回字节类型
    3.文件操作
    4.图片处理
    5.处理每个url地址
    6.对页面分类进行处理
    """

    def __init__(self, page):
        self.url = "https://www.jianshu.com"  # 简书首页
        self.headers = {
            'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) "
                          "Chrome/67.0.3396.99 Safari/537.36"}
        self.proxy = {
            'http': 'http://username:pasword@47.96.239.158:3128',
            'https': 'http://username:pasword@47.96.239.158:3128'
        }
        self.page = page  # 文章页码
        self.subjects_url = []  # 主题页面地址list
        self.subjects_title = []  # 主题类别list

    def get_response(self, response_address):
        """主要用作获取响应体,response_address需传入url,返回值为字节流"""

        # 返回字节流文本
        return requests.get(response_address, headers=self.headers, proxies=self.proxy).content

    def file_operation(self):
        """文件操作"""
        # 1.创建保存数据的文件夹
        if not os.path.exists("简书" + sep + "images"):
            os.makedirs("简书" + sep + "images")
            return

        # 2.创建分类的json文件
        self.classsubject = open("./简书/主题分类.json", "w+", encoding="utf-8")

        # 3.创建文章简要json文件:标题,链接
        self.article_briefly = open("./简书/文章简要.json", "a+", encoding="utf-8")

    def image_processing(self, image_list):
        """图片保存下载,需要传入值image_list图片的url地址"""

        # 1.图片名字,以'/'切割
        file_name = [name.split(altsep)[-1] for name in image_list]

        # 2.image二进制列表
        image_bytes = [self.get_response(image_url) for image_url in image_list]

        # 3.保存下载图片
        try:
            # 如果图片名字列表中有数据
            if len(file_name) != 0:
                with open("./简书/images" + "/" + file_name.pop(0), "wb+") as image:
                    image.write(image_bytes.pop(0))
            # 我不想捕获一个已知的异常,"pop from empty list"
            else:
                return
        except Exception as error:
            print(error)
            pass

    def regular_matching_collections(self, topics_page=None, subject_html=None, article_page=None,
                                     article_detail_html=None,
                                     child_article_detail_html=None):
        """url,正则匹配处理集合
        topics_page传入热门专题分类页码,默认为空
        subject_html传入文章列表页面的源码,默认为空
        article_page传入热门专题分类页面页码,默认为空
        article_detail_html文章列表页面页码,默认为空
        child_article_detail_html子文章详情页面页码,默认为空
        参数不能空
        """

        if topics_page:
            # 1.更多热门专题地址页面url
            return self.url + "/recommendations/collections?page={}&order_by=recommend".format(
                topics_page)

        elif article_page:
            # 2.主题文章页面url,简书的前端文章采用的是滚动加载动画   https://www.jianshu.com/c/ad41ba5abc09
            return '?order_by=added_at&page={}'.format(article_page)

        elif subject_html:
            # 3.主题分类  href="/c/  <h4 class="name">(.+)</h4>
            # 手动对分类链接进行去重
            subject_url_before = re.findall(r'href="(/c/\w+)', subject_html)  # 未去重之前的地址list
            subject_url_list = list(set(re.findall(r'href="(/c/\w+)', subject_html)))  # 去重后顺序错误的地址list
            subject_url_list.sort(key=subject_url_before.index)  # 排序

            title_list = re.findall(r'<h4 class="name">(.+)</h4>', subject_html)  # 分类title
            return title_list, subject_url_list

        elif article_detail_html:
            # 4.文章详情地址及文章标题  href="/p/a253c7be2a90">只有烂程序员才相信世界是由技术驱动的</a>
            return re.findall(r'href="(/\w/\w+)">(.+)</a>', article_detail_html)

        elif child_article_detail_html:
            # 5.图片url地址  data-original-src="https://img.haomeiwen.com/i10014062/0fd9c472bee8702d.jpg
            return re.findall(
                r'data-original-src="(//upload-images.jianshu.io/upload_images/\d{8}-\w+.+)" data-orignal-width',
                child_article_detail_html)
        else:
            raise ValueError("参数不能为空!")

    def theme_subjects(self):
        """主题分类"""

        # 热门分类页面
        hot_html_list = [self.get_response(self.regular_matching_collections(topics_page=i + 1)).decode() for i in
                         range(2)]

        # 获取热门分类的公众号title和url地址
        list_tuple = [self.regular_matching_collections(subject_html=hot_html_list[i]) for i in range(2)]

        # 重新整合热门分类title和url
        title_list = list_tuple[0][0] + list_tuple[1][0]
        url_list = list_tuple[0][1] + list_tuple[1][1]

        # title和url写入文件中,使用标题表作为这个字典的键,转换为枚举类型取下标
        subjects_dict = {}
        for index, value in enumerate(title_list):
            subjects_dict[value] = self.url + url_list[index]

        # 转换为json
        subjects_json = json.dumps(subjects_dict, ensure_ascii=False, indent=4)
        self.classsubject.write(subjects_json)

    def article_details(self):
        """文章简要,文章标题和url"""

        # 读取主题分类json文件
        self.classsubject.seek(0, 0)
        load_dict = json.loads(self.classsubject.read())

        # 读取主题分类字典,取出字典的值,组成页面地址的list
        for keys, values in load_dict.items():
            self.subjects_title.append(keys)
            self.subjects_url.append(values)

        dumps_dict = {}  # 进行字典拼装,创建外部字典

        # 先提取所有公众号的地址,采集每个页面里文章里的title和url
        for index, url in enumerate(self.subjects_url):

            dumps_dict[self.subjects_title[index]] = {}  # 取出公众号的标题作为字典的键

            # 进入某一分类的页面
            for page in range(self.page):

                # 拼接url并且返回页面
                html = self.get_response(url + self.regular_matching_collections(article_page=page + 1)).decode()

                # 获取页面里文章title和url,接收返回值文章list
                article_list = self.regular_matching_collections(article_detail_html=html)

                # 准备写入文章简要json文件, 文章地址,文章标题
                for article_url, title in article_list:
                    dumps_dict[self.subjects_title[index]][title] = self.url + article_url

        # 转换为json
        subjects_json = json.dumps(dumps_dict, ensure_ascii=False, indent=4)
        self.article_briefly.write(subjects_json)

        # self.classsubject.close()
        # self.article_briefly.close()

        print("OK")

    def pic_downloader(self):
        """图片下载器"""

        # 进入文章子页面,读取文章简要json文件
        self.article_briefly.seek(0, 0)
        load_dict = json.loads(self.article_briefly.read())

        for keywords in load_dict.keys():

            # 提取字典中的文章的地址
            single_page = load_dict[keywords]  # 单个分类的所有文章标题和url
            del single_page[keywords]  # 删除多余的键值对

            # 取出字典剩下所有的地址值,得到所有文章的源代码
            page_list = [self.get_response(self.url + url).decode() for url in single_page.values()]

            # 下载图片,图片地址list
            image_url_list = [self.regular_matching_collections(child_article_detail_html=html) for html in page_list]

            # 保存图片
            for image_list in image_url_list:
                self.image_processing(image_list)

        # self.article_briefly.close()



def main():
    """主程序"""
    run = SimpleBook(1)  # page是控制文章列表的页码
    run.file_operation()
    run.theme_subjects()
    run.article_details()
    run.pic_downloader()
    run.classsubject.close()
    run.article_briefly.close()
    pass


if __name__ == '__main__':
    main()

regular_matching_collections不是很满意,使用了很多if,elif,有思路的大兄弟,望留言

相关文章

网友评论

    本文标题:实现简书数据爬取

    本文链接:https://www.haomeiwen.com/subject/fkmlvftx.html