安装

pip install -i https://pypi.tuna.tsinghua.edu.cn/simple scrapy==2.11.2

创建工程

假设我要创建一个名叫 myspider 的 scrapy 工程,他的功能是爬取 url=https://baidu.com 的数据,需要运行以下命令:

# 创建工程文件夹并初始化整个scrapy工程
scrapy startproject myspider
# 进入工程文件夹
cd myspider
# 创建爬虫,语法如下: scrapy genspider 爬虫名称 需要爬的域名
scrapy genspider spider baidu.com 

完成以上步骤之后,工程目录如下:

myspider
├── myspider
│   ├── __init__.py
│   ├── __pycache__
│   │   ├── __init__.cpython-312.pyc
│   │   └── settings.cpython-312.pyc
│   ├── items.py
│   ├── middlewares.py
│   ├── pipelines.py
│   ├── settings.py
│   └── spiders
│       ├── __init__.py
│       ├── __pycache__
│       │   └── __init__.cpython-312.pyc
│       └── spider.py
└── scrapy.cfg

下面我们先来编写一个简单的案例,借此来说明这些文件如何配置使用

一个简单的工程

编写数据解析

首先打开 myspider/spiders/spider.py,内容应该如下:

import scrapy


class SpiderSpider(scrapy.Spider):
    name = "spider" # 爬虫名字,后面运行要用
    allowed_domains = ["baidu.com"]
    start_urls = ["https://baidu.com"]

    def parse(self, response):
        pass

我们需要修改 parse 函数,这里是对爬取页面的数据解析函数,假设在这里我通过 xpath 爬取百度首页的下图部分内容:

class SpiderSpider(scrapy.Spider):
    name = "spider"
    allowed_domains = ["baidu.com"]
    start_urls = ["https://baidu.com"]

    def parse(self, response):
        a_list = response.xpath("//div[@class='s-top-left-new s-isindex-wrap']/a")[:8]

        id = 0

        for a in a_list:
            text = a.xpath("./text()").extract_first()

            text = text.strip()
            dic = {
                "id": id,
                "text": text
            }

            id += 1

            yield dic

pipelines.py 中打印结果:

class MyspiderPipeline:
    def process_item(self, item, spider):
        print(item)
        return item

注意:

spider返回的内容只能是字典, requestes对象, item数据或者None. 其他内容一律报错

运行爬虫

# 运行爬虫美丽如下 scrapy crawl 爬虫名字
scrapy crawl spider

结果如下:

编写数据保存

在数据被抓去后,会传递到 pipeline,下面来看一下 pipeline 中如何处理数据:

  1. 首先要修改 settings.py 中的配置

    # 数据处理管道,这里可以填写多个
    ITEM_PIPELINES = {  
       "game.pipelines.GamePipeline": 300,  
       "game.pipelines.MySQLPipeline": 300  
    }
    
  2. pipelines.py 文件中编写数据存储代码,这里给出两个官方示例:

    # 存储到json
    import json
    
    from itemadapter import ItemAdapter
    
    class JsonWriterPipeline:
        def open_spider(self, spider):
            self.file = open("items.jsonl", "w")
    
        def close_spider(self, spider):
            self.file.close()
    
        def process_item(self, item, spider):
            line = json.dumps(ItemAdapter(item).asdict()) + "\n"
            self.file.write(line)
            return item
    
    ```python
    # 存储到Database
    import pymongo
    from itemadapter import ItemAdapter
    
    class MongoPipeline:
        collection_name = "scrapy_items"
    
        def __init__(self, mongo_uri, mongo_db):
            self.mongo_uri = mongo_uri
            self.mongo_db = mongo_db
    
        @classmethod
        def from_crawler(cls, crawler):
            return cls(
                mongo_uri=crawler.settings.get("MONGO_URI"),
                mongo_db=crawler.settings.get("MONGO_DATABASE", "items"),
            )
    
        def open_spider(self, spider):
            self.client = pymongo.MongoClient(self.mongo_uri)
            self.db = self.client[self.mongo_db]
    
        def close_spider(self, spider):
            self.client.close()
    
        def process_item(self, item, spider):
            self.db[self.collection_name].insert_one(ItemAdapter(item).asdict())
            return item
    

自定义数据传输结构 item

在上述案例中, 我们使用字典作为数据传递的载体, 但是如果数据量非常大. 由于字典的key是随意创建的. 极易出现问题, 此时再用字典就不合适了. Scrapy中提供item作为数据格式的声明位置. 我们可以在 items.py 文件提前定义好该爬虫在进行数据传输时的数据格式. 然后再写代码的时候就有了数据名称的依据了。

import scrapy


class MyspiderItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    pass

class BaiduItem(scrapy.Item):
    id = scrapy.Field()
    text = scrapy.Field()

spider.py 中运用如下:

import scrapy
from myspider.items import BaiduItem

class SpiderSpider(scrapy.Spider):
    name = "spider"
    allowed_domains = ["baidu.com"]
    start_urls = ["https://baidu.com"]

    def parse(self, response):
        a_list = response.xpath("//div[@class='s-top-left-new s-isindex-wrap']/a")[:8]

        id = 0

        for a in a_list:
            text = a.xpath("./text()").extract_first()

            text = text.strip()

            item = BaiduItem()

            item["id"] = id
            item["text"] = text

            id += 1

            yield item

两次运行结果一致: