Scrapy爬取知乎日报,并将文章保存为pdf

目标:

在D:/知乎日报下有两个文件夹,latest存放最新爬下来的文章,past存放之前爬下来的文章

在下一次爬的时候,如果文章已经爬过,就不再下载,如果没有就存放到latest中,并将之前已经存放在latest中的文章转移到past中


所用库,scrapy(必须的),pdfkit(用于html到pdf的转换),os和shutil(处理文件)

首先在http://daily.zhihu.com/得到每一篇文章的url和title,通过title判断这篇文章是否已经下载过。

将没下载过的文章的url传递给pdfkit.from_url,则自动在本地路径生成一个pdf。


代码:


在settings.py中声明两个变量filename和url

# -*- coding: utf-8 -*-
#
# Define here the models for your scraped items
#
# See documentation in:
# http://doc.scrapy.org/en/latest/topics/items.html

import scrapy


class ZhihudailyItem(scrapy.Item):
    filename = scrapy.Field()
    url = scrapy.Field()
    pass

在爬虫文件中爬取daily.zhihu.com,返回文章的url和title给pipeline文件

# -*- coding: utf-8 -*-
import scrapy
from zhihudaily.items import ZhihudailyItem

class ZhihuSpider(scrapy.Spider):
    name = 'zhihu'
    allowed_domains = ['daily.zhihu.com']
    start_urls = ['http://daily.zhihu.com/']

    def parse(self, response):
        for col in range(1,4):
            for row in range(1,11):
                item = ZhihudailyItem()
                xpath_selector = "//div[@class='col-lg-4'][{0}]//div[@class='wrap'][{1}]".format(col, row)
                sub_url = response.xpath(xpath_selector+"//@href").extract_first()
                fix_url = "http://daily.zhihu.com"
                item["url"] = fix_url+sub_url

                title  = response.xpath(xpath_selector+"//span/text()").extract_first()
                item["filename"] =  "D:/知乎日报/"+title+".pdf"
                #print(item["filename"], item['url'])
                yield item
        #pdfkit.from_url(mix_url, filename)

        

在pipeline.py中将文章保存为pdf,并存放到对应的文件夹。并给出相应的提示。

# -*- coding: utf-8 -*-

# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: http://doc.scrapy.org/en/latest/topics/item-pipeline.html

import pdfkit
import os
import shutil
class ZhihudailyPipeline(object):
    def process_item(self, item, spider):
        try:
            filename = os.path.basename(item["filename"])
            dirname = os.path.dirname(item["filename"])

            if not self.file_exsists(filename, dirname):
                print('*'*20)
                print(filename, "不存在")
                pdfkit.from_url(item["url"], dirname+r'/latest/'+filename)
            else:
                print('*'*20)
                print(filename, "已存在")
                for _r, _d, files in os.walk("D:/知乎日报/latest/"):
                    if filename in files:
                        shutil.move("D:/知乎日报/latest/"+filename, "D:/知乎日报/past/")
                        print(filename,  "文件移到past\n")
                print('*'*20)
                print('\n') 

        except:
            # 此处一个Exit with code 1 due to network error: ContentNotFoundError异常
            # 此异常为是因为css文件引用了外部的资源,如:字体,图片,iframe加载等。
            # 选择忽略此异常
            # 
            # 触发此异常,说明调用了pdfkit.from_url
            print(item["filename"], "download successfully")
            print('*'*20)
            print('\n')
            pass        

        return item

    def file_exsists(self, filename, dirname):
        for root, dirs, files in os.walk(dirname):
            if filename in files:
                return True
        return False

最后在settings.py中取消pipeline的注释,并设置USER_AGENT

# Crawl responsibly by identifying yourself (and your website) on the user-agent
USER_AGENT = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3269.3 Safari/537.36'

保存文件,scrapy crawl zhihu 运行测试一下:



貌似还可以,看结果





基本功能实现了,但是并不太实用,继续学习。

### 使用 Scrapy 实现乎网页抓取 #### 自定义 Downloader Middleware 处理反爬虫机制 为了成功爬取乎网站的数据,可以利用 Scrapy 的 `Downloader Middleware` 来模拟浏览器行为设置必要的请求头。通过这种方式能够有效规避乎的反爬虫策略。 ```python from scrapy import signals class CustomMiddleware(object): def process_request(self, request, spider): request.headers['User-Agent'] = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36' request.headers['authorization'] = 'oauth c3cef7c66a1843f8b3a9e6a1e3160e20' ``` 上述代码展示了如何创建一个自定义中间件类,在其中设置了 User-Agent 和 authorization 字段[^3]。 #### 定义 Item 存储结构 在项目目录下的 items.py 文件中需定义存储数据所需的字段。以下是针对乎问答的具体实现: ```python import scrapy class ZhihuAnswerItem(scrapy.Item): zhihu_id = scrapy.Field() url = scrapy.Field() question = scrapy.Field() author_id = scrapy.Field() content = scrapy.Field() praise_num = scrapy.Field() comments_num = scrapy.Field() create_time = scrapy.Field() update_time = scrapy.Field() crawl_time = scrapy.Field() ``` 这段代码定义了一个用于存储乎回答详情的对象模型[^2]。 #### JSON 数据解析与翻页逻辑 乎 API 返回的结果通常是以 JSON 格式的字符串形式存在,因此需要编写相应的解析函数来获取所需的信息,判断是否有下一页链接继续抓取。 ```python def parse(self, response): json_data = response.json() for data in json_data.get('data'): answer_item = ZhihuAnswerItem( zhihu_id=data.get('id'), url=data.get('url'), question=data.get('question').get('title') if isinstance(data.get('question'), dict) else None, author_id=data.get('author').get('name') if isinstance(data.get('author'), dict) else None, content=data.get('content'), praise_num=data.get('voteup_count'), comments_num=data.get('comment_count') ) yield answer_item next_page_url = json_data.get('paging', {}).get('next') is_end = json_data.get('paging', {}).get('is_end') if not is_end and next_page_url: yield scrapy.Request(url=next_page_url, callback=self.parse) ``` 这里实现了对单条记录的提取以及分页加载的功能[^4]。 #### 数据持久化至 MySQL 最后一步就是把采集下来的内容存入关系型数据库MySQL当中。这可以通过 pipelines 功能模块完成配置工作。 ```python import pymysql.cursors class MysqlPipeline(): def __init__(self): self.connect = pymysql.connect(host='localhost', port=3306, user='root', password='', db='zhihudata', charset='utf8mb4', cursorclass=pymysql.cursors.DictCursor) def close_spider(self, spider): self.connect.close() def process_item(self, item, spider): try: with self.connect.cursor() as cursor: sql = """INSERT INTO answers(zhihu_id,url,question,author_id,content,praise_num,comments_num,crawl_time) VALUES (%s,%s,%s,%s,%s,%s,%s,NOW())""" cursor.execute(sql,(item["zhihu_id"],item["url"],item["question"],item["author_id"],item["content"],item["praise_num"],item["comments_num"])) self.connect.commit() except Exception as e: print(e) return item ``` 以上脚本完成了从连接建立到最终关闭整个过程中的每一条记录入库操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值