Scrapy爬取知乎Python专题精华,连答主头像都给爬下来,不放过一切

前些天写的一篇"我与Scrapy的初次相识,理论+实战入门Scrapy "都上首页推荐了,到现在浏览量还没破百,难受。
写的不好大概,不过只要有一人看,都是鼓励,一直写,一直积淀,终成大佬,给自己加油。


言归正传,直入主题,用Scrapy爬取知乎Python专题精华,连答主的头像都给爬下来,不放过一切,片草不留。

实在话,答主的头像爬下来也不知道干嘛,主要是想练习以下两点:

  1. Scrapy爬取图片,并重命名图片(即重写imagePipeline)
  2. Scrapy多个Item多个Pipeline的相关处理

从百度进知乎的专题曲曲折折的,大家直接点我这链接去:https://www.zhihu.com/topic/19552832/top-answers
在这里插入图片描述
写代码? 不急,首要的是先创建一个Scrapy项目了,框架有了,后面才能添砖加瓦。在终端依次输入指令。

# 创建项目
scrapy startproject zhihu

# 进入到项目目录下
cd zhihu

# 创建Spider文件 python_zhihu为项目名 zhihu.com为限定的爬取网域
scrapy genspider python_zhihu zhihu.com

1.分析网页,找到数据在哪

1.1、是不是异步加载?

数据无非是在html文件或者是异步加载出来的,我发现知乎这个专题精华没有分页栏,往下拉加载数据URL地址也没有发生变化,断定这个网页是个异步加载,我们打开开发者工具去XHR文件中查找。

1.2、网页里数据的结构是什么样的?

刚刷新网页那会,出来的文件确实没有什么可用的数据,但是伴随着网页往下滚动加载数据,出来了有用的数据了,如下可以看出这个文件返回的正是这文章josn格式的相关数据。这是Preview模块,方便查看返回了数据的结构。
在这里插入图片描述
这里我们就可以编写Item的代码了。
分析json数据,编写Item。
在这里插入图片描述

我这里就不贴代码了,放个截图,这代码真得自己敲了才有印象,若是有哪里不清楚可以来讨论一下的,最后搞不定的情况(必定有细节错误)再发源码。

特意说明:爬取头像的Item中image_urlsimages这两个是Scrapy自带的Image管道默认的名称,尽可能不要修改,另外一个image_name是为了重写ImagePipeline,修改图片的名称才加进去的,后面再说。
在这里插入图片描述

1.3、请求的网址呢?

知道了上面的json数据离爬取是进了一步,那我们需要知道如何请求到这些数据,去这个文件的Headers模块查看,以下是截图。

不知道大家有没有看到这api几字,这里调用api来获取数据再方便不过了,通过URL末尾的offsetlimit两个参数来控制获取的页数和每页的数据量。
在这里插入图片描述

2.构建Spider爬虫,爬取数据

2.1、解析response,解析出数据

和普通的网络爬虫不一样,Scrapy的下载器已经下载好了网页,我们只需要解析它的response就行了。
在这里插入图片描述
这里有两个try-except,主要是解决掉下面两个问题。
问题1:返回的json数据中,不全是文章的数据,我没细分析,不清楚那些个无关的数据是哪的。
在这里插入图片描述
问题2:有些用户是匿名回答的,或者已经注销了账号了,没有详情页链接的,爬取时把它设置成”未知用户“。
在这里插入图片描述
因为每次返回的json数据不尽是文章内容,所以逻辑上,到这里至少能够爬取一条的文章的信息。

2.2、测试发现的问题

插一句,之前为了初步测试一下,我先把piplines和settings两个文件配置起来了(这里测试的我就不细说了,主要说一下出现的问题)。

文本信息爬取倒是没事,安然无恙,这使用自带的ImagePipline倒是出了如下两个问题,已解决。
问题1: ModuleNotFoundError: No module named ‘scrapy.contrib’
在这里插入图片描述
在这里插入图片描述
解决方案:
在这里插入图片描述

问题2: raise ValueError(‘Missing scheme in request url: %s’ % self._url)
在这里插入图片描述
解决方案:
原本是下图中的String类型,但是image_urls要求的是list类型,所以强转一下。
在这里插入图片描述
修改为
在这里插入图片描述

另外图片爬取下来后,有如下一个缺陷,Scrapy自带的图片管道下载的图片名称是它们URL的 SHA1 hash值,不是很好看,所以要重写它的ImagePipeline,修改图片的名称。
在这里插入图片描述

2.3、piplines和settings的设置

上一步插了一句测试,这步要重归正题,讲一下pipelines和settings的配置了,不然有些人要犯迷糊了。
piplines文件中设置了两个pipline分别对Items中的两个item进行处理。
另外因为Scrapy传item是一股脑就都传来了,这两个pipline分不清楚哪个是自己需要处理的item,直接运行会报错误,所以在函数接收item后要对item进行辨别,使用 isinstance() 方法对item进行判断类型相同与否。

值得一提的是,这些个Pipline当中的函数名都是默认的,我们只是对它进行重写,所以不能修改函数名称。
在这里插入图片描述
settings文件 一配置完,这个项目真就完全可以跑起来了。

ITEM_PIPELINES里每个pipeline后面的整数型数值是指它运行的优先级,或者说距离,数值越小,距离越近,越先执行。

在这里插入图片描述

2.4、翻页请求下一页怎么做?

前面配置了item、spider、piplines、settings四个文件,项目也就完成,但是之前也就只爬取了一页的数据,所以,我们还要在Spider中的parse函数中加上翻页请求下一页的代码。
在这里插入图片描述
这里给大家伙看一眼最后的结果,好叫大家伙安心,这个代码是可以运行的。
在这里插入图片描述

以下为自娱自乐环节

Q:当真不贴源码,你写的博文全是图,不方便借鉴呀

A:当真不发,图上注释也很清楚了,自己敲更有印象,看看代码截图再自己思考一下更有收获

Q:其实我知道你想发源码的,不然整这么大的加粗黑字在这里干嘛

A:好吧,被你发现了,是担心自己有些细节没表达好,导致大家伙最终的没爬到数据

Q:不对不对,你必定还有别的想法,爬虫主要是思路,每个网页的结构不同,学会一个代码只能说会了一个爬虫代码而已,要学会思路自己去分析才是王道

A:知己呀,还是你懂我,我其实就是想让那些个看见没源代码的童鞋早早的走,不要误了他们学习其他有源码的。

Q:哈哈哈哈,快行动起来,早知你有诈,我前面没看就翻到文末来了

A:哎呀,可恶,道高一尺魔高一丈,不过我这里还是要给好好学习的童鞋们贴上源码的,让他们好好研究,不要错了什么细节,尽情享用

Spider

# -*- coding: utf-8 -*-
import scrapy
import json
from ..items import ZhihuItem, ZhihuUserImageItem


class PythonZhihuSpider(scrapy.Spider):
    name = 'python_zhihu'
    start_urls = ['https://www.zhihu.com/api/v4/topics/19552832/feeds/essence?include=data%5B%3F(target.type%3Dtopic_sticky_module)%5D.target.data%5B%3F(target.type%3Danswer)%5D.target.content%2Crelationship.is_authorized%2Cis_author%2Cvoting%2Cis_thanked%2Cis_nothelp%3Bdata%5B%3F(target.type%3Dtopic_sticky_module)%5D.target.data%5B%3F(target.type%3Danswer)%5D.target.is_normal%2Ccomment_count%2Cvoteup_count%2Ccontent%2Crelevant_info%2Cexcerpt.author.badge%5B%3F(type%3Dbest_answerer)%5D.topics%3Bdata%5B%3F(target.type%3Dtopic_sticky_module)%5D.target.data%5B%3F(target.type%3Darticle)%5D.target.content%2Cvoteup_count%2Ccomment_count%2Cvoting%2Cauthor.badge%5B%3F(type%3Dbest_answerer)%5D.topics%3Bdata%5B%3F(target.type%3Dtopic_sticky_module)%5D.target.data%5B%3F(target.type%3Dpeople)%5D.target.answer_count%2Carticles_count%2Cgender%2Cfollower_count%2Cis_followed%2Cis_following%2Cbadge%5B%3F(type%3Dbest_answerer)%5D.topics%3Bdata%5B%3F(target.type%3Danswer)%5D.target.annotation_detail%2Ccontent%2Chermes_label%2Cis_labeled%2Crelationship.is_authorized%2Cis_author%2Cvoting%2Cis_thanked%2Cis_nothelp%3Bdata%5B%3F(target.type%3Danswer)%5D.target.author.badge%5B%3F(type%3Dbest_answerer)%5D.topics%3Bdata%5B%3F(target.type%3Darticle)%5D.target.annotation_detail%2Ccontent%2Chermes_label%2Cis_labeled%2Cauthor.badge%5B%3F(type%3Dbest_answerer)%5D.topics%3Bdata%5B%3F(target.type%3Dquestion)%5D.target.annotation_detail%2Ccomment_count%3B&offset=0&limit=10']

    def parse(self, response):
        item = ZhihuItem()
        imageItem = ZhihuUserImageItem()

        datas = json.loads(response.body)['data']
        for data in datas:
            try:
                item['id'] = data['target']['id']
                item['title'] = data['target']['title']
                item['url'] = data['target']['url']
                item['content'] = data['target']['content']
                item['voteup_count'] = data['target']['voteup_count']
                item['comment_count'] = data['target']['comment_count']

                item['author_name'] = data['target']['author']['name']
                try:
                    item['author_url'] = 'https://www.zhihu.com/' + data['target']['author']['user_type'] + data['target']['author']['url_token']
                except Exception as e:
                    item['author_url'] = '未知用户'
                    pass
                yield item

                imageItem['image_urls'] = [data['target']['author']['avatar_url']]
                imageItem['image_name'] = data['target']['author']['name']
                yield imageItem

            except Exception as e:
                pass

        # 进行下几页的爬取
        url = 'https://www.zhihu.com/api/v4/topics/19552832/feeds/essence?include=data%5B%3F(target.type%3Dtopic_sticky_module)%5D.target.data%5B%3F(target.type%3Danswer)%5D.target.content%2Crelationship.is_authorized%2Cis_author%2Cvoting%2Cis_thanked%2Cis_nothelp%3Bdata%5B%3F(target.type%3Dtopic_sticky_module)%5D.target.data%5B%3F(target.type%3Danswer)%5D.target.is_normal%2Ccomment_count%2Cvoteup_count%2Ccontent%2Crelevant_info%2Cexcerpt.author.badge%5B%3F(type%3Dbest_answerer)%5D.topics%3Bdata%5B%3F(target.type%3Dtopic_sticky_module)%5D.target.data%5B%3F(target.type%3Darticle)%5D.target.content%2Cvoteup_count%2Ccomment_count%2Cvoting%2Cauthor.badge%5B%3F(type%3Dbest_answerer)%5D.topics%3Bdata%5B%3F(target.type%3Dtopic_sticky_module)%5D.target.data%5B%3F(target.type%3Dpeople)%5D.target.answer_count%2Carticles_count%2Cgender%2Cfollower_count%2Cis_followed%2Cis_following%2Cbadge%5B%3F(type%3Dbest_answerer)%5D.topics%3Bdata%5B%3F(target.type%3Danswer)%5D.target.annotation_detail%2Ccontent%2Chermes_label%2Cis_labeled%2Crelationship.is_authorized%2Cis_author%2Cvoting%2Cis_thanked%2Cis_nothelp%3Bdata%5B%3F(target.type%3Danswer)%5D.target.author.badge%5B%3F(type%3Dbest_answerer)%5D.topics%3Bdata%5B%3F(target.type%3Darticle)%5D.target.annotation_detail%2Ccontent%2Chermes_label%2Cis_labeled%2Cauthor.badge%5B%3F(type%3Dbest_answerer)%5D.topics%3Bdata%5B%3F(target.type%3Dquestion)%5D.target.annotation_detail%2Ccomment_count%3B&limit=10&offset={}'

        page = 10

        for i in range(5, 15 + 10*int(page), 10):
            yield scrapy.Request(url=url.format(i), callback=self.parse)

items

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

# Define here the models for your scraped items
#
# See documentation in:
# https://doc.scrapy.org/en/latest/topics/items.html

import scrapy


class ZhihuItem(scrapy.Item):
    id = scrapy.Field()
    title = scrapy.Field()
    url = scrapy.Field()
    content = scrapy.Field()
    voteup_count = scrapy.Field()
    comment_count = scrapy.Field()
    author_name = scrapy.Field()
    author_url = scrapy.Field()
    pass


# 爬取用户头像的Item
class ZhihuUserImageItem(scrapy.Item):
    image_urls = scrapy.Field()
    images = scrapy.Field()
    image_name = scrapy.Field()
    pass

Piplines

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

# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://doc.scrapy.org/en/latest/topics/item-pipeline.html
from scrapy import Request
from scrapy.exceptions import DropItem
from scrapy.pipelines.images import ImagesPipeline
import csv
import re

from .items import ZhihuItem
from .items import ZhihuUserImageItem


class ZhihuPipeline(object):
    def __init__(self):
        file = './data.csv'
        self.file = open(file, 'a+', encoding="utf-8", newline='')
        self.writer = csv.writer(self.file, dialect="excel")

    def process_item(self, item, spider):
        if isinstance(item, ZhihuItem):
            item['content'] = re.sub('<.*?>', '', re.sub('</p>', '\n', item['content']))
            self.writer.writerow([item['id'], item['title'], item['url'], item['content'],
                                  item['voteup_count'], item['comment_count'], item['author_name'], item['author_url']])
            print('已进入文本管道')
        return item

    def close_spider(self, spider):
        self.file.close()


class UserImagePipeline(ImagesPipeline):
    def get_media_requests(self, item, info):
        if isinstance(item, ZhihuUserImageItem):
            for image_url in item['image_urls']:
                yield Request(image_url, meta={'name': item['image_name']})

    def item_completed(self, results, item, info):
        if isinstance(item, ZhihuUserImageItem):
            image_path = [x['path'] for ok, x in results if ok]
            if not image_path:
                raise DropItem('Item contains no images')
            print('已进入图片管道')
        return item

    def file_path(self, request, response=None, info=None):
        image_name = request.meta['name']
        filename = image_name + '.jpg'
        return filename

Settings

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

# Scrapy settings for zhihu project
#
# For simplicity, this file contains only settings considered important or
# commonly used. You can find more settings consulting the documentation:
#
#     https://doc.scrapy.org/en/latest/topics/settings.html
#     https://doc.scrapy.org/en/latest/topics/downloader-middleware.html
#     https://doc.scrapy.org/en/latest/topics/spider-middleware.html

BOT_NAME = 'zhihu'

SPIDER_MODULES = ['zhihu.spiders']
NEWSPIDER_MODULE = 'zhihu.spiders'

LOG_LEVEL = 'ERROR'

# Crawl responsibly by identifying yourself (and your website) on the user-agent
#USER_AGENT = 'zhihu (+http://www.yourdomain.com)'
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36'

# Obey robots.txt rules
ROBOTSTXT_OBEY = False

# Configure item pipelines
# See https://doc.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
    'zhihu.pipelines.ZhihuPipeline': 300,
    'zhihu.pipelines.UserImagePipeline': 10,
}

IMAGES_STORE = 'images'
IMAGES_EXPIRES = 90
  • 8
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
### 回答1: Python爬虫Scrapy是一种用于爬取网站数据的工具。它可以自动化地从网站上获取数据,并将其存储在本地或远程服务器上。Scrapy使用Python编写,具有高效、灵活和可扩展的特点,可以处理大量数据和复杂的网站结构。使用Scrapy可以快速地获取所需的数据,从而为数据分析和挖掘提供支持。 ### 回答2: Python爬虫是一种快速、高效的网络爬虫工具,它利用Python语言及其强大的第三方库实现自动化爬取互联网信息的过程。而Scrapy是一个Python爬虫框架,可以帮助我们快速构建持续性的爬虫应用程序。 基于Scrapy框架的爬虫程序设计,首先需要明确自己的目标:确定目标网站、确定目标页面、确定目标信息。其次,在编写程序之前,我们需要构建好爬虫的数据模型,包括目标页面的结构、目标信息的抽取规则等。Scrapy框架的核心就是Spider,将会快速地处理目标页面,解析数据并且提取出所需信息。这些信息可以经过清洗、存储和分析等处理过程,最终达到我们的预期需求。 爬虫程序的要步骤是: 1.创建一个Scrapy项目,包括spider、items、pipelines等。 2.定义spider,包括start_urls、parse等。 3.定义item,表示解析结果的结构。 4.定义pipeline,用于处理抓取到的数据。 5.运行爬虫程序并保存数据。 在使用Scrapy进行网络爬虫时,需要遵守相关法律法规,尊重目标网站的利益和知识产权,不进行违法、违规的操作,同时遵守robots.txt协议等规定,如不得爬取不允许抓取的页面。 总之,对于想要进行网络数据采集的人来说,Scrapy是一个非常强大的Python爬虫框架。它支持异步IO和协程等功能,可以快速、高效地提取目标数据。但是,使用Scrapy时需要遵循规则,合理利用,不进行违法、违规操作。 ### 回答3: Scrapy是基于Python语言的爬虫框架,它可以更好的帮我们实现爬取网站的任务。scrapy提供了一整套爬虫流程和一些基本机制,比如:异步处理,中间件机制,数据管道,数据解析等等。这些机制可以帮我们更加简便和高效地进行网络爬取任务。 使用Scrapy爬取网站有以下几个步骤: 1. 安装Scrapy框架 我们需要提前安装好PythonScrapy框架。安装Scrapy比较方便,可以通过pip来安装,命令为:pip install scrapy。 2. 创建scrapy爬虫工程 我们需要使用scrapy startproject 项目名的命令来创建项目,并进入到项目的工程目录来创建爬虫内容,命令为:scrapy genspider 爬虫名 域名。 3. 配置scrapy爬虫的设置 进入到Scrapy工程目录,找到settings.py文件,修改里面的文件配置,包括:User-Agent,爬虫间隔时间,ip代理池设置等等。 4. 配置scrapy爬虫的项 在Spider中定义需要爬取的网站信息,包括URL地址和需要获取的数据。 5. 实现URL地址的解析 在Spider中写解析函数,对爬虫获取到的URL进行解析,获取需要的数据内容。 6. 爬虫启动 最后,使用命令scrapy crawl 爬虫名,启动爬虫。爬取的URL资源会被输出进行。如果爬虫爬取的URL过多,那么可以使用命令scrapy crawl 爬虫名 -o 文件名.格式名来保存爬虫获取到的数据内容。 总的来说,Scrapy是一个快速高效的爬虫框架,有着多种策略来处理不同种类的爬虫需求。使用Scrapy进行爬虫的开发,不仅能够为我们省去很多重复性工作,还能够提高我们的开发效率和爬虫的运行效率。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值