【学习笔记】爬虫框架Scrapy入门

一. Scrapy简介、架构、数据流和项目结构

二. Scrapy入门

1. Scrapy架构由哪些部分组成?

1.1 Scrapy简介

Scrapy是:由Python语言开发的一个快速、高层次的屏幕抓取和web抓取框架,用于抓取web站点并从页面中提取结构化的数据。

Scrapy依赖包

Scrapy 是用纯python编写的,它依赖于几个关键的python包(以及其他包):

  • lxml 一个高效的XML和HTML解析器

  • parsel ,一个写在lxml上面的html/xml数据提取库,

  • w3lib ,用于处理URL和网页编码的多用途帮助程序

  • twisted 异步网络框架

  • cryptography 和 pyOpenSSL ,处理各种网络级安全需求

1.2 Scrapy架构

  • Engine。引擎,处理整个系统的数据流处理、触发事务,是整个框架的核心。

  • Item。项目,它定义了爬取结果的数据结构,爬取的数据会被赋值成该Item对象。

  • Scheduler。调度器,接受引擎发过来的请求并将其加入队列中,在引擎再次请求的时候将请求提供给引擎。

  • Downloader。下载器,下载网页内容,并将网页内容返回给蜘蛛。

  • Spiders。蜘蛛,其内定义了爬取的逻辑和网页的解析规则,它主要负责解析响应并生成提取结果和新的请求。

  • Item Pipeline。项目管道,负责处理由蜘蛛从网页中抽取的项目,它的主要任务是清洗、验证和存储数据。

  • Downloader Middlewares。下载器中间件,位于引擎和下载器之间的钩子框架,主要处理引擎与下载器之间的请求及响应。

  • Spider Middlewares。蜘蛛中间件,位于引擎和蜘蛛之间的钩子框架,主要处理蜘蛛输入的响应和输出的结果及新的请求。

1.3 数据流

Scrapy中的数据流由引擎控制,数据流的过程如下。

  • Engine首先打开一个网站,找到处理该网站的Spider,并向该Spider请求第一个要爬取的URL。

  • Engine从Spider中获取到第一个要爬取的URL,并通过Scheduler以Request的形式调度。

  • Engine向Scheduler请求下一个要爬取的URL。

  • Scheduler返回下一个要爬取的URL给Engine,Engine将URL通过Downloader Middlewares转发给Downloader下载。

  • 一旦页面下载完毕,Downloader生成该页面的Response,并将其通过Downloader Middlewares发送给Engine。

  • Engine从下载器中接收到Response,并将其通过Spider Middlewares发送给Spider处理。

  • Spider处理Response,并返回爬取到的Item及新的Request给Engine。

  • Engine将Spider返回的Item给Item Pipeline,将新的Request给Scheduler。

  • 重复第二步到最后一步,直到Scheduler中没有更多的Request,Engine关闭该网站,爬取结束。

通过多个组件的相互协作、不同组件完成工作的不同、组件对异步处理的支持,Scrapy最大限度地利用了网络带宽,大大提高了数据爬取和处理的效率。

1.4 项目结构

Scrapy框架和pyspider不同,它是通过命令行来创建项目的,代码的编写还是需要IDE。项目创建之后,项目文件结构如下所示:

 这里各个文件的功能描述如下。

  • scrapy.cfg:它是Scrapy项目的配置文件,其内定义了项目的配置文件路径、部署相关信息等内容。

  • items.py:它定义Item数据结构,所有的Item的定义都可以放这里。

  • pipelines.py:它定义Item Pipeline的实现,所有的Item Pipeline的实现都可以放这里。

  • settings.py:它定义项目的全局配置。

  • middlewares.py:它定义Spider Middlewares和Downloader Middlewares的实现。

  • spiders:其内包含一个个Spider的实现,每个Spider都有一个文件。

 note:在指定文件夹下打开cmd窗口,输入 tree/f 命令可以查看该文件夹结构

2. Scrapy入门

先安装以下两个库,注意这些库可能有版本更新,旧版本可能会报错,及时安装新版本,尤其是fake-useragent库。

pip install scrapy            # 构建和运行网络爬虫的强大的 Python 框架
pip install fake-useragent    # 生成伪装用户代理的 Python 库,这里先安装,后面会讲到

Note: 推荐在anaconda prompt和anaconda powershell prompt窗口中执行如下命令,或者在cmd窗口的base环境中运行,cmd窗口中激活base环境命令如下:

conda activate base

否则如下创建scrapy项目的命令会报错,如下所示,提示默认conda环境未激活

C:\Users\sun78\Desktop\视频 python\9>python
Python 3.8.8 (default, Apr 13 2021, 15:08:03) [MSC v.1916 64 bit (AMD64)] :: Anaconda, Inc. on win32

Warning:
This Python interpreter is in a conda environment, but the environment has
not been activated.  Libraries may fail to load.  To activate this environment
please see https://conda.io/activation

Type "help", "copyright", "credits" or "license" for more information.
>>>

2.1 创建项目

安装完成后就可以使用scrapy命令来创建项目,如下

scrapy startproject sougouspider  # gousearchspide就是项目名

如下述树状文件结构所示,创建的sousouspider项目文件夹下面包含scrapy.cfg文件和sougouspider子文件夹,子文件夹中包含items.py,middlewares.py,pipelines.py等文件以及spiders子孙文件夹。

2.2 创建Spider

Spider是自己定义的Class,Scrapy用它来从网页里抓取内容,并解析抓取的结果。不过这个Class必须继承Scrapy提供的Spider类scrapy.Spider,还要定义Spider的名称和起始请求,以及怎样处理爬取后的结果的方法。

也可以使用命令行创建一个Spider。比如要生成quotes这个Spider,可以执行如下命令:

cd sougouspider # 进入项目文件夹中的sougouspider子文件夹
scrapy genspider sgspider weixin.sogou.com  # sgspider为spider名称,后是网站域名

 进入刚才创建的sougouspider文件夹,然后执行genspider命令。第一个参数是Spider的名称,第二个参数是网站域名。执行完毕之后,spiders文件夹中多了一个sgspider.py,它就是刚刚创建的Spider

 sgspider内容如下所示:

import scrapy


class SgspiderSpider(scrapy.Spider):
    name = 'sgspider'
    allowed_domains = ['weixin.sogou.com']
    start_urls = ['http://weixin.sogou.com/']

    def parse(self, response):
        pass

这里有三个属性——nameallowed_domainsstart_urls,还有一个方法parse

  • name,它是每个项目唯一的名字,用来区分不同的Spider。

  • allowed_domains,它是允许爬取的域名,如果初始或后续的请求链接不是这个域名下的,则请求链接会被过滤掉。

  • start_urls,它包含了Spider在启动时爬取的url列表,初始请求是由它来定义的。

  • parse,它是Spider的一个方法。默认情况下,被调用时start_urls里面的链接构成的请求完成下载执行后,返回的响应就会作为唯一的参数传递给这个函数。该方法负责解析返回的响应、提取数据或者进一步生成要处理的请求。

2.3 创建Item

Item是保存爬取数据的容器,它的使用方法和字典类似。不过,相比字典,Item多了额外的保护机制,可以避免拼写错误或者定义字段错误。

创建Item需要继承scrapy.Item,并且定义类型为scrapy.Field的字段。观察目标网站,我们可以获取到到内容有url,page等。

scrapy默认创建的items.py内容如下

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

import scrapy


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

定义Item,此时将items.py修改如下:

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

import scrapy


class SougouspiderItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    visit_url = scrapy.Field()
    page = scrapy.Field()
    rank = scrapy.Field()
    title = scrapy.Field()
    # pass

 其中visit_url,page,rank和 title 是 sgspider中定义的变量

2.4 修改settings.py

创建sougouspider默认生成的settings.py的内容如下

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

BOT_NAME = 'sougouspider'

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


# Crawl responsibly by identifying yourself (and your website) on the user-agent
#USER_AGENT = 'sougouspider (+http://www.yourdomain.com)'

# Obey robots.txt rules
ROBOTSTXT_OBEY = True

# Configure maximum concurrent requests performed by Scrapy (default: 16)
#CONCURRENT_REQUESTS = 32

# Configure a delay for requests for the same website (default: 0)
# See https://docs.scrapy.org/en/latest/topics/settings.html#download-delay
# See also autothrottle settings and docs
#DOWNLOAD_DELAY = 3
# The download delay setting will honor only one of:
#CONCURRENT_REQUESTS_PER_DOMAIN = 16
#CONCURRENT_REQUESTS_PER_IP = 16

# Disable cookies (enabled by default)
#COOKIES_ENABLED = False

# Disable Telnet Console (enabled by default)
#TELNETCONSOLE_ENABLED = False

# Override the default request headers:
#DEFAULT_REQUEST_HEADERS = {
#   'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
#   'Accept-Language': 'en',
#}

# Enable or disable spider middlewares
# See https://docs.scrapy.org/en/latest/topics/spider-middleware.html
#SPIDER_MIDDLEWARES = {
#    'sougouspider.middlewares.SougouspiderSpiderMiddleware': 543,
#}

# Enable or disable downloader middlewares
# See https://docs.scrapy.org/en/latest/topics/downloader-middleware.html
#DOWNLOADER_MIDDLEWARES = {
#    'sougouspider.middlewares.SougouspiderDownloaderMiddleware': 543,
#}

# Enable or disable extensions
# See https://docs.scrapy.org/en/latest/topics/extensions.html
#EXTENSIONS = {
#    'scrapy.extensions.telnet.TelnetConsole': None,
#}

# Configure item pipelines
# See https://docs.scrapy.org/en/latest/topics/item-pipeline.html
#ITEM_PIPELINES = {
#    'sougouspider.pipelines.SougouspiderPipeline': 300,
#}

# Enable and configure the AutoThrottle extension (disabled by default)
# See https://docs.scrapy.org/en/latest/topics/autothrottle.html
#AUTOTHROTTLE_ENABLED = True
# The initial download delay
#AUTOTHROTTLE_START_DELAY = 5
# The maximum download delay to be set in case of high latencies
#AUTOTHROTTLE_MAX_DELAY = 60
# The average number of requests Scrapy should be sending in parallel to
# each remote server
#AUTOTHROTTLE_TARGET_CONCURRENCY = 1.0
# Enable showing throttling stats for every response received:
#AUTOTHROTTLE_DEBUG = False

# Enable and configure HTTP caching (disabled by default)
# See https://docs.scrapy.org/en/latest/topics/downloader-middleware.html#httpcache-middleware-settings
#HTTPCACHE_ENABLED = True
#HTTPCACHE_EXPIRATION_SECS = 0
#HTTPCACHE_DIR = 'httpcache'
#HTTPCACHE_IGNORE_HTTP_CODES = []
#HTTPCACHE_STORAGE = 'scrapy.extensions.httpcache.FilesystemCacheStorage'

修改后的settings.py

BOT_NAME = 'sougouspider'

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

# Obey robots.txt rules
ROBOTSTXT_OBEY = False # 将False改为True

# Disable cookies (enabled by default)
COOKIES_ENABLED = True  # 将False改为True


# Configure item pipelines
# See https://docs.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
    'sougouspider.pipelines.SougouspiderPipeline': 300,
}

REDIRECT_ENABLED = False
HTTPERROR_ALLOWED_CODES = [302]


注意:pipelines.py和middlewares.py文件使用原默认内容。关于__init__.py , 在Python工程里,当python检测到一个目录下存在__init__.py文件时,python就会把它当成一个模块(module)。__init__.py可以是一个空文件,也可以有非常丰富的内容。

2.5 修改默认生成的spider文件

修改后的sgspider.py


from sougouspider.items import SougouspiderItem
from IP.free_ip import get_random_proxy
from IP.get_cookies import get_new_cookies,get_new_headers
import scrapy
import time
import random
from tqdm import tqdm 


class Sgspiderspider(scrapy.Spider):
    name = 'sgspider'
    allowed_domains = ['weixin.sogou.com']
    start_urls = ['https://weixin.sogou.com/weixin?&query=Python爱好者社区&type=2&ie=utf8']
    def start_requests(self):
        headers = get_new_headers()
        print('请求的headers',headers)        
        for url in self.start_urls:
            # 获取代理IP
            proxy = 'http://' + str(get_random_proxy())
            print('请求的proxy',proxy)
            yield scrapy.Request(url=url,
                                 callback=self.parse,
                                 headers=headers,
                                 meta={'http_proxy': proxy})

    def parse(self, response):
        headers_new = get_new_headers()
        print('第二次请求的headers',headers_new)
        cookies_new = get_new_cookies()
        print('请求的cookies_new',cookies_new)
        
        # 获取当前页码
        current_page = int(response.xpath('//div[@id="pagebar_container"]/span/text()').extract_first())
        print('response的内容',response)
        print('reponse成功被解析,此时的页码',current_page)
        # 解析当前页面
        #for i, a in enumerate(response.xpath('//div[contains(@class,"news-box")]/div[@class="txt-box"]/h3/a')):
        for i, a in enumerate(response.xpath('//div[contains(@class,"txt-box")]/h3/a')):
            # 获取标题,去除空格和换行符
            # title = ''.join(a.xpath('./em/text() | ./text()').extract()).replace(' ', '').replace('\n', '')
            print('i和a分别为',i,a)
            title = a.xpath('.//text()').extract()  # 获取文本字符串列表
# string(.)函数会得到所指元素的所有节点文本内容,这些文本讲会被拼接成一个字符串
            # title = a.xpath('string(.)')
            print('页面标题是',title)
            if title:
                item = SougouSpiderItem()
                # 获取访问链接(①非跳转链接②跳转链接)、页码、行数、标题
                if a.xpath('@href').extract_first().startswith('/link'):
                    item['visit_url'] = 'https://weixin.sogou.com' + a.xpath('@href').extract_first()  # 提取链接
                else:
                    item['visit_url'] = a.xpath('@href').extract_first()
                item['page'] = current_page
                item['rank'] = i + 1
                item['title'] = title
                yield item
        # 控制爬取频率
        #time_wait=random.randint(8, 10)
        #print('等待时间',time_wait)        
        #time.sleep(time_wait)        
        time_wait=random.randint(40, 50)
        for i in tqdm(range(time_wait)):           
            time.sleep(0.2)
        # 获取“下一页”的链接
        p = response.xpath('//div[@id="pagebar_container"]/a[@id="sogou_next"]')
        print('准备解析下一页的网址',p)
        if p:
            p_url = 'https://weixin.sogou.com/weixin' + str(p.xpath('@href').extract_first())
            print('下一页的网址',p_url)
            proxy = 'http://' + str(get_random_proxy())
            yield scrapy.Request(url=p_url,
                                 callback=self.parse,
                                 headers=headers_new,
                                 cookies=cookies_new,
                                 meta={'http_proxy': proxy})

 为了应对网站的反爬虫,采用了更换动态ip和伪装header的措施,采用如下代码将更换动态ip和伪装header的模块导入到在上述spider文件中。(将模块命名为IP,将free_ip.py和get_cookies.py放入该IP文件夹中)

from IP.free_ip import get_random_proxy
from IP.get_cookies import get_new_cookies,get_new_headers

free_ip.py

# coding=utf-8
import requests

proxypool_url = 'http://127.0.0.1:5555/random'

def get_random_proxy():
    response = requests.get(proxypool_url)
    try:
        if response.status_code == 200:
            return response.text.strip()
    except ConnectionError:
        return None

if __name__ == '__main__':
    print(get_random_proxy())

注意:上述文件中使用了ip代理池,代理池的构建参考【学习笔记】WSL2、Docker以及docker-compose安装及环境配置_bu懂就问的博客-CSDN博客_wsl2和docker

 利用scrapy进行爬虫之前需要开启docker,并在代理池相应文件夹下(本案例中是ProxyPool-master文件夹)打开cmd命令行窗口,运行以下命令才能够在 http://127.0.0.1:5555/random 端口获取代理ip,上述free_ip.py才能够正常执行

docker-compose up

崔庆才代理池github地址如下,github地址

get_cookies.py

# coding=utf-8
from IP.free_ip import get_random_proxy
from fake_useragent import UserAgent
import requests

ua = UserAgent().random

def get_new_headers():
    headers = {"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
              "Accept-Encoding": "gzip, deflate, br",
              "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
              "User-Agent": ua}
    return headers

def get_new_cookies():
    url = 'https://v.sogou.com/v?ie=utf8&query=&p=40030601'
    proxies = {"http": "http://" + get_random_proxy()}
    headers = {'User-Agent': ua}
    rst = requests.get(url=url,
                       headers=headers,
                       allow_redirects=False,
                       proxies=proxies)
    cookies = rst.cookies.get_dict()
    return cookies

if __name__ == '__main__':
    print(get_new_cookies())

注意:上述get_cookies.py函数是为了生成cookie信息,其中除了导入get_random_proxy()函数生成代理ip之外,还从fake_useragent库导入了UserAgent函数,user-agent可以由fake_useragent随机生成。cookie中的部分信息是基于代理ip访问搜狗视频获取的。

fake_useragent库的安装可以使用如下命令,github地址

pip install fake-useragent

2.6 运行scrapy框架

本案例在运行之前需要

1. 开启docker软件

2. 在ProxyPool-master文件夹中执行 docker-compose up 获取动态代理ip

本案例中ProxyPool-master文件夹位于

D:\software\ProxyPool-master

3. 修改项目爬虫文件中的查询关键词。本案例中,爬虫文件位于

C:\Users\sun78\Desktop\视频 python\scrapy_peform\sougouspider\sougouspider\spiders

4. 之后可以在scrapy项目文件夹中执行下述命令

scrapy crawl sgspider -o XXX.json  # XXX.json是爬虫生成的json文件名

scrapy crawl sgspider -o 1.json    #推荐使用1.json命名

5. 运行完之后会将items.py中的变量以字典的形式保存为.json文件,其中中文信息尚未进行utf8转码,可以编写python脚本进一步进行数据清洗和转换。下面的脚本将其转换成markdown链接格式。执行以下命令

python conver_json.py

conver_json.py


import json

filename = 'Python爱好者社区.json'

with open(filename) as f_obj:
    numbers=json.load(f_obj)
    print(len(numbers))
    for i in range(len(numbers)):
        e = numbers[i]
        print('['+' '.join(e['title'])+']'+'('+ e['visit_url']+ ')'+'\n')
        with open('Python爱好者社区.txt', 'a') as new_file:
    #        new_file.write(str(e['title'])+'('+ e['visit_url']+ ')'+'\n')
            new_file.write('['+' '.join(e['title'])+']'+'('+ e['visit_url']+ ')'+'\n')
    #
        #print(i)
        
        

参考资料和链接

【scrapy爬虫】最新sogou搜狗搜索 机智操作绕过反爬验证码(搜狗微信公众号文章同理)_彡千的博客-CSDN博客

GitHub - hellysmile/fake-useragent: up to date simple useragent faker with real world database

GitHub - Python3WebSpider/ProxyPool: An Efficient ProxyPool with Getter, Tester and Server

Scrapy框架的使用之Scrapy框架介绍

崔庆才. python3网络爬虫开发实战

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值