banner
bladedragon

bladedragon

scrapy爬虫入门

现在说到爬虫,大家都会或多或少地将 python 和爬虫联系在一起,归根到底,是因为 python 丰富的生态和灵活简单的语法。同时基于 python 存在有几个强大的爬虫框架,极大地降低了爬虫的难度,提高了编写程序的效率。最近我也体验了一下 scrapy,算是做一个入门的记录吧

简介#

Scrapy 是一个用于爬网网站和提取结构化数据的应用程序框架,可用于各种有用的应用程序,例如数据挖掘,信息处理或历史档案。

—— 翻译自官网

推荐直接查看官网,里面甚至有完整的 scraoy 教程,简直是深入学习框架的必备选择!

安装#

在安装 scrapy 框架的时候,网上有许多方法,针对 python 的环境不同,可能存在奇奇怪怪的错误,这里我基于 python3 环境,预装了 pip,亲测一遍过

预装环境

Windows 10 + python 3.7.0 + pip20.1 +virtualenv

同时请开启一个新的 virtual 环境来保证不会出现其他包的依赖冲突

前期安装组件

  • lxml
  • pyOpenSSL
  • Twisted
  • PyWin32

安装 lxml

直接pip安装即可。这是python一个HTMLXML解析库,即使不用框架也是经常使用的

pip3 install lxml

安装 PyWin32

官网下载对应版本的安装包双击安装即可 [pywin32]([https://sourceforge.net/projects/pywin32/files/pywin32/Build%20221/](https://sourceforge.net/projects/pywin32/files/pywin32/Build 221/))

安装剩余组件

这里首先要介绍wheel

wheelpython的一个打包格式,以前python主流的打包格式是.egg文件,但现在*.whl文件也变得流行起来。

wheel其实上是 python 上一种压缩打包的组件,有点类似于 zip 之类的,但在本文中你只要知道通过 wheel 文件格式文件你可以快速将一个库安装到你的python环境中

安装其实也很简单

pip3 install wheel

这样你的python环境就支持.whl文件格式的安装啦

接下来的步骤就是到各个官网上下载各组件的whl格式,注意要和你的 python 环境匹配

安装

pip3 install pyOpenSSL-19.1.0-py2.py3-none-any.whl 
  • Twisted注意要和你的 python 版本对应

像我的环境就是

pip3 install Twisted-20.3.0-cp37-cp37m-win_amd64.whl

安装 scrapy

所有依赖包安装成功后直接 pip 安装 scrapy 就不会有问题啦

pip3 install Scrapy

组件介绍#

首先创建项目

在你想要放置爬虫项目的文件夹运行,xxx 就是你的项目名

scrapy startproject xxx

顺便记录一下一些基本的操作

  • 创建项目:scrapy startproject xxx
  • 进入项目:cd xxx #进入某个文件夹下
  • 创建爬虫:scrapy genspider xxx(爬虫名) xxx.com (爬取域)
  • 生成文件:scrapy crawl xxx -o xxx.json (生成某种类型的文件)
  • 运行爬虫:scrapy crawl XXX
  • 列出所有爬虫:scrapy list
  • 获得配置信息:scrapy settings [options]

创建完成后你可以看到文件夹下多了这些内容

image

让我们一个个介绍这些组件(spider_demo是你的爬虫项目名)

  • scrapy.cfg: 项目的配置文件 (在项目文件夹的平行目录下)
  • spider_demo/spiders/: 放置spider代码的目录. (放爬虫的地方)也是你放爬虫具体逻辑的地方
  • spider_demo/items.py: 项目中的item文件.(创建容器的地方,也是定义最终爬虫得到的结果格式的地方)
  • spider_demo/pipelines.py: 项目中的pipelines文件.(实现数据的清洗、存储和验证)
  • spider_demo/settings.py: 项目的设置文件.(爬虫的具体配置,包括启动某个中间件,启动关闭某个功能等)
  • spider_demo/middlewares.py: 定义项目的下载器中间件和爬虫中间件

感觉是不是还有点蒙圈?接下来简单介绍一下scrapy运行的原理,这样相信就能更理解这些组件的作用了


官网的流程图

崎 architecture 的建筑

Scrapy是由执行引擎控制执行的

  1. Spider发起请求给Engine
  2. Engine安排请求Scheduler和接受下一个爬取请求
  3. Scheduler返回下一个请求
  4. Engine将请求通过Downloader Middlewares发送给Downloader
  5. Downloader爬取网页并将返回结果通过Downloader Middlewares发送回Engine
  6. 引擎接受响应并通过Spider Middleware转发给Spider处理
  7. Spider parse()方法对获取到的response进行处理,解析出items或者请求,将解析出来的items或请求,返回给Engine
  8. Engineitems 发送到Item Pipline, 将请求发送到Scheduler
  9. 重复步骤 1 直到没有新的请求

总结一下上面步骤出现的组件

组件名组件功能
Engine框架核心,负责整体的数据和信号的调度框架实现
Scheduler一个存放请求的队列框架实现
Downloader执行具体下载任务的单元框架实现
Spider处理下载得到的响应结果,提取需要的是数据(具体的业务逻辑)自己实现
Item Pipline处理最终得到的数据,如进行持久化操作自己实现
Downloader MIddlewares在正式进行下载任务之前,可以进行一些自定义处理。比如设置请求头,设置代理自己实现
Spider Midderwares自定义请求和过滤响应自己实现

相信这一套组合拳下来应该能对这个框架有了基本的认识,接下来就通过实战来强化一下记忆吧

简单应用#

这次是根据网上通过爬取猫眼电影的排行榜做的一个demo,以后有时间再换一个更加复杂的demo

实现目标是爬取电影排行榜上的片名、分数和排名,同时将结果以json的格式保存在一个.json文件中

image

首先你要确定你需要爬取哪些数据,将你需要的数据记录到容器中,在item.py中进行编写:

import scrapy

#这里我们需要排名、标题、收藏人数、上映时间和分数
class SpiderDemoItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    index = scrapy.Field()
    title = scrapy.Field()
    star = scrapy.Field()
    releasetime = scrapy.Field()
    score = scrapy.Field()

接下来在Spiders文件夹下新建一个爬虫文件,例如我新建了一个MoyanSpider.py文件

import scrapy
from spider_demo.items import SpiderDemoItem


class MaoyanSpider(scrapy.Spider):
    #这是爬虫启动的名称,之后启动爬虫就需要用到这个名称
    name = "maoyan"
    #可以爬取的域名可选列表
    allowed_domains = ["maoyan.com"]
    #目标爬取的网址
    start_urls = [
        "http://maoyan.com/board/7/", 
        "http://maoyan.com/board/4/",
    ]

    #处理已经下载的页面
    def parse(self, response):
        
        dl = response.css(".board-wrapper dd")
        #通过解析得到具体的数据存到容器中
        for dd in dl:
            item = SpiderDemoItem()
            item["index"] = dd.css(".board-index::text").extract_first()
            item["title"] = dd.css(".name a::text").extract_first()
            item["star"] = dd.css(".star::text").extract_first()
            item["releasetime"] = dd.css(".releasetime::text").extract_first()
            score = dd.css('.integer::text').extract_first()
            if score is not None:
                item["score"] = score  + dd.css('.fraction::text').extract_first()
            else:
                item["score"] = 0

            #通过yield将结果返回
            yield item

这里提一下,scrapy支持各种类型的解析,你可以使用 python 常见的三大解析库进行解析,但框架也提供了一种自己的解析方式(Selector

  • 选择器
  • Xpath

这里不详细叙述,以后有时间再细聊

同时我们需要在setting,py中对配置稍微进行修改 (setting.py中有许多默认配置,这里只展示修改的部分)

#如果没有自动生成UA,就需要手动定义,但是每次爬取都是同样的UA容易出现验证操作,因此后面还会介绍一种随机生成UA的方法
USER_AGENT = 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36'

#允许机器人协议,关于机器人协议的具体内容可以自行上网查找
ROBOTSTXT_OBEY = False

这样,基本就完成了一个简单的爬虫,只需要执行scrapy crawl maoyan(最后一个是你的爬虫名)

image

之后还有两个要点,一个是项目的持久化,一个是随机化User-Agent

先看持久化,这里简单起见就示范将爬虫数据以json格式导出

这里需要修改pipline.py,至于为什么,相信看了之前的组件介绍应该能明白

import json
import codecs


class SpiderDemoPipeline:
    def process_item(self, item, spider):
        return item


class JsonPipline(object):
    def __init__(self):
        print("打开文件,准备写入....")
        self.file = codecs.open("maoyan.json", "wb", encoding='utf-8')

    def process_item(self, item, spider):
        print("准备写入...")
        line = json.dumps(dict(item), ensure_ascii=False) + "\n"
        self.file.write(line)
        return item

    def close_spider(self, spider):
        print("写入完毕,关闭文件")
        self.file.close

然后在setting.py中开启自定义的pipline

ITEM_PIPELINES = {
    #    'spider_demo.pipelines.SpiderDemoPipeline': 300,
    'spider_demo.pipelines.JsonPipline': 200
}

至于随机化 UA,先说明添加 UA 的原理

scrapy首先会读取setting.py里面关于 UA 的设置,然后经过middleware,如果没有进行自定义操作,就会将配置中的 UA 添加到请求头中。因此,想要实现随机化 UA, 实际上就可以在发起网页请求之前,在Download Middleware上做文章。

这里在middleware.py上做了修改,引入了第三方包fake_useragent

from scrapy import signals
import random
from fake_useragent import UserAgent


class RandomUserAgentMiddleware(object):
    # 随机更换user-agent
    def __init__(self, crawler):
        super(RandomUserAgentMiddleware, self).__init__()
        self.ua = UserAgent()
        self.ua_type = crawler.settings.get("RANDOM_UA_TYPE", "random")

    @classmethod
    def from_crawler(cls, crawler):
        return cls(crawler)

    def process_request(self, request, spider):
        def get_ua():
            return getattr(self.ua, self.ua_type)

        request.headers.setdefault('User-Agent', get_ua())

同时在setting.py上做修改

DOWNLOADER_MIDDLEWARES = {
    'spider_demo.middlewares.SpiderDemoDownloaderMiddleware': 543,
     'spider_demo.middlewares.RandomUserAgentMiddleware': 400,
    'scrapy.downloadermiddleware.useragent.UserAgentMiddleware': None,
}

# 随机选择UA
#这个是自己设置的,依赖于fake-useragent
RANDOM_UA_TYPE = 'random'

至此,一个简单的爬虫应用就实现了!

可以看见UA发生了变化

image

同时生成了一个maoyan.json文件

json 文件

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.