【Python爬虫】学习笔记(三)

Python3.8

Python3.8

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

Scrapy

Scrapy的介绍与安装

Srcapy学习文档

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

尽管 Scrapy 最初是为网络抓取而设计的,但它也可用于使用 API提取数据或用作通用网络爬虫。

Scrapy的优势

  • 可以容易构建大规模的爬虫项目
  • 内置re、xpath、css选择器
  • 可以自动调整爬行速度
  • 开源和免费的网络爬虫框架
  • 可以快速导出数据文件: JSON,CSV和XML
  • 可以自动方式从网页中提取数据(自己编写规则)
  • Scrapy很容易扩展,快速和功能强大
  • 这是一个跨平台应用程序框架(在Windows,Linux,Mac OS)
  • Scrapy请求调度和异步处理

Scrapy的架构

最简单的单个网页爬取流程是 spiders > scheduler > downloader > spiders > item pipeline

省略了engine环节

各部件介绍

  • 引擎(engine)

    • 用来处理整个系统的数据流处理, 触发事务(框架核心)
  • 调度器(Scheduler)

    • 用来接受引擎发过来的请求, 压入队列中, 并在引擎再次请求的时候返回. 可以想像成一个URL(抓取网页的网址或者说是链接)的优先队列, 由它来决定下一个要抓取的网址是什么, 同时去除重复的网址
  • 下载器(Downloader)

    • 用于下载网页内容, 并将网页内容返回给蜘蛛(Scrapy下载器是建立在twisted这个高效的异步模型上的)
  • 爬虫(Spiders)

    • 爬虫是主要干活的, 用于从特定的网页中提取自己需要的信息, 即所谓的实体(Item)。用户也可以从中提取出链接,让Scrapy继续抓取下一个页面
  • 项目管道(Pipeline)

    • 负责处理爬虫从网页中抽取的实体,主要的功能是持久化实体、验证实体的有效性、清除不需要的信息。当页面被爬虫解析后,将被发送到项目管道,并经过几个特定的次序处理数据。
  • 下载器中间件(Downloader Middlewares)

    • 位于Scrapy引擎和下载器之间的框架,主要是处理Scrapy引擎与下载器之间的请求及响应
  • 爬虫中间件(Spider Middlewares)

    • 介于Scrapy引擎和爬虫之间的框架,主要工作是处理蜘蛛的响应输入和请求输出
  • 调度中间件(Scheduler Middewares)

    • 介于Scrapy引擎和调度之间的中间件,从Scrapy引擎发送到调度的请求和响应

Scracp的安装:

pip install scrapy

Scrapy_创建第一个爬虫项目

注意:在进行任何一个爬虫成程序之前,要确认该网站是否运行被爬取,可以通过/robots.txt文件来查询,比如:https://www.baidu.com/robots.txt

在终端中输入:

scrapy startproject myfrist(project_name)

然后会自动生成如下的文件:

                                

文件说明:

名称作用
scrapy.cfg

项目的配置信息,主要为Scrapy命令行工具提供一个基础的配置信息。(真正爬虫相关的配置信息在settings.py文件中)

items.py设置数据存储模板,用于结构化数据,如:Django的Model
pipelines数据处理行为,如:一般结构化的数据持久化
settings.py配置文件,如:递归的层数、并发数,延迟下载等
spiders爬虫目录,如:创建文件,编写爬虫规则

然后创建一个爬虫:(注意:一般创建爬虫文件时,以网站域名命名)

scrapy genspider 爬虫名 爬虫的地址

爬虫包含的内容:

  • name: 它定义了蜘蛛的唯一名称
  • allowed_domains: 它包含了蜘蛛抓取的基本URL;
  • start-urls: 蜘蛛开始爬行的URL列表;
  • parse(): 这是提取并解析刮下数据的方法;

代码示例:

import scrapy
class DoubanSpider(scrapy.Spider):
  name = 'douban'
  allowed_domains = 'douban.com'
  start_urls = [
    'https://movie.douban.com/top250/'
   ]
  def parse(self, response):
    movie_name = response.xpath("//div[@class='item']//a/span[1]/text()").extract()
    movie_core = response.xpath("//div[@class='star']/span[2]/text()").extract()
    yield {
      'movie_name':movie_name,
      'movie_core':movie_core
     }

 Scrapy_启动方式介绍

方法一:Scrapy命令运行,在命令行中实现

启动项目格式如下:

scrapy crawl 爬虫名

(这里的爬虫名是爬虫文件中name属性的值)

有时这样子启动会遇到一些问题,导致命令无法生效:

解决方法就是,通过cd命令切换到项目目录中,正常运行就可以了

还有一种方法:

scrapy runspider spider_file.py

注意:这里是爬虫文件的名字,要指定到spider文件夹

方法二:python脚本启动

在项目的目录下,创建脚本,比如项目名为:scrapy01,创建脚本的路径为scrapy01\scrapy01\脚本.py

脚本1:

from scrapy.cmdline import execute
execute(['scrapy', 'crawl', '爬虫名字'])

脚本2:

from scrapy.crawler import CrawlerProcess
from spiders.baidu import BaiduSpider
process = CrawlerProcess()
process.crawl(BaiduSpider)
process.start()

脚本3:

from twisted.internet import reactor
from spiders.baidu import BaiduSpider
from spiders.taobao import TaoBaoSpider
from scrapy.crawler import CrawlerRunner
from scrapy.utils.log import configure_logging

configure_logging() # 开启日志出输出
runner = CrawlerRunner()
runner.crawl(BaiduSpider)
runner.crawl(TaoBaoSpider)
d = runner.join()
d.addBoth(lambda _: reactor.stop())
reactor.run()

然后在命令行中运行:

python 脚本.py

Scrapy_控制台日志信息介绍

在启动Scrapy时,程序会自动输出日志,内容图下:

  • 启动爬虫

  • 使用的模块与版本

  • 加载配置文件

  • 打开下载中间件

  • 打开中间件

  • 打开管道

  • 爬虫开启

  • 打印统计

2030-07-13 16:45:19 [scrapy.utils.log] INFO: Scrapy 2.6.1 started (bot: scrapy02)
2030-07-13 16:45:19 [scrapy.utils.log] INFO: Versions: lxml 4.8.0.0, libxml2 2.9.12, cssselect 1.1.0, parsel 1.6.0, w3lib 1.22.0, Twisted 22.4.0, Python 3.10.2 (tags/v3.10.2:a58ebcc, Jan 17 2022, 14:12:15) [MSC v.1929 64 bit (AMD64)], pyOpenSSL 22.0.0 (OpenSSL 3.0.4 21 Jun 2022), cryptography 37.0.3, Platform Windows-10-10.0.22000-SP0
2030-07-13 16:45:19 [scrapy.crawler] INFO: Overridden settings:
{'BOT_NAME': 'scrapy02',
 'NEWSPIDER_MODULE': 'scrapy02.spiders',
 'SPIDER_MODULES': ['scrapy02.spiders']}
2030-07-13 16:45:19 [scrapy.utils.log] DEBUG: Using reactor: twisted.internet.selectreactor.SelectReactor
2030-07-13 16:45:19 [scrapy.extensions.telnet] INFO: Telnet Password: a7b76850d59e14d0
2030-07-13 16:45:20 [scrapy.middleware] INFO: Enabled extensions:
['scrapy.extensions.corestats.CoreStats',
 'scrapy.extensions.telnet.TelnetConsole',
 'scrapy.extensions.logstats.LogStats']
2030-07-13 16:45:20 [scrapy.middleware] INFO: Enabled downloader middlewares:
['scrapy.downloadermiddlewares.httpauth.HttpAuthMiddleware',
 'scrapy.downloadermiddlewares.downloadtimeout.DownloadTimeoutMiddleware',
 'scrapy.downloadermiddlewares.defaultheaders.DefaultHeadersMiddleware',
 'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware',
 'scrapy.downloadermiddlewares.retry.RetryMiddleware',
 'scrapy.downloadermiddlewares.redirect.MetaRefreshMiddleware',
 'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware',
 'scrapy.downloadermiddlewares.redirect.RedirectMiddleware',
 'scrapy.downloadermiddlewares.cookies.CookiesMiddleware',
 'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware',
 'scrapy.downloadermiddlewares.stats.DownloaderStats']
2030-07-13 16:45:20 [scrapy.middleware] INFO: Enabled spider middlewares:
['scrapy.spidermiddlewares.httperror.HttpErrorMiddleware',
 'scrapy.spidermiddlewares.offsite.OffsiteMiddleware',
 'scrapy.spidermiddlewares.referer.RefererMiddleware',
 'scrapy.spidermiddlewares.urllength.UrlLengthMiddleware',
 'scrapy.spidermiddlewares.depth.DepthMiddleware']
2030-07-13 16:45:20 [scrapy.middleware] INFO: Enabled item pipelines:
[]
2030-07-13 16:45:20 [scrapy.core.engine] INFO: Spider opened
2030-07-13 16:45:20 [scrapy.extensions.logstats] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min)
2030-07-13 16:45:20 [scrapy.extensions.telnet] INFO: Telnet console listening on 127.0.0.1:6023
2030-07-13 16:45:24 [filelock] DEBUG: Attempting to acquire lock 1733280163264 on D:\python_env\spider2_env\lib\site-packages\tldextract\.suffix_cache/publicsuffix.org-tlds\de84b5ca2167d4c83e38fb162f2e8738.tldextract.json.lock
2030-07-13 16:45:24 [filelock] DEBUG: Lock 1733280163264 acquired on D:\python_env\spider2_env\lib\site-packages\tldextract\.suffix_cache/publicsuffix.org-tlds\de84b5ca2167d4c83e38fb162f2e8738.tldextract.json.lock
2030-07-13 16:45:24 [filelock] DEBUG: Attempting to release lock 1733280163264 on D:\python_env\spider2_env\lib\site-packages\tldextract\.suffix_cache/publicsuffix.org-tlds\de84b5ca2167d4c83e38fb162f2e8738.tldextract.json.lock
2030-07-13 16:45:24 [filelock] DEBUG: Lock 1733280163264 released on D:\python_env\spider2_env\lib\site-packages\tldextract\.suffix_cache/publicsuffix.org-tlds\de84b5ca2167d4c83e38fb162f2e8738.tldextract.json.lock
2030-07-13 16:45:24 [scrapy.core.engine] DEBUG: Crawled (200) <GET http://www.baidu.com/> (referer: None) 
1111111111111111111111111111111111111111111111
2030-07-13 16:45:24 [scrapy.core.engine] INFO: Closing spider (finished)
2030-07-13 16:45:24 [scrapy.statscollectors] INFO: Dumping Scrapy stats:
{'downloader/request_bytes': 213,
 'downloader/request_count': 1,
 'downloader/request_method_count/GET': 1,
 'downloader/response_bytes': 1476,
 'downloader/response_count': 1,
 'downloader/response_status_count/200': 1,
 'elapsed_time_seconds': 4.716963,
 'finish_reason': 'finished',
 'finish_time': datetime.datetime(2030, 7, 13, 8, 45, 24, 923094),
 'httpcompression/response_bytes': 2381,
 'httpcompression/response_count': 1,
 'log_count/DEBUG': 6,
 'log_count/INFO': 10,
 'response_received_count': 1,
 'scheduler/dequeued': 1,
 'scheduler/dequeued/memory': 1,
 'scheduler/enqueued': 1,
 'scheduler/enqueued/memory': 1,
 'start_time': datetime.datetime(2030, 7, 13, 8, 45, 20, 206131)}
2030-07-13 16:45:24 [scrapy.core.engine] INFO: Spider closed (finished)

Scrapy_数据的提取

Scrapy有自己的数据提取机制,被称为选择器

通过Selector对象获取:

正常使用:

response.selector.xpath('//span/text()').get()
response.selector.css('span::text').get()
response.selector.re('<span>')

快捷使用

response.xpath('//span/text').get()
response.css('span::text').get()

创建对象:

from scrapy.selector import Selector

通过text参数 初始化

body = '<html><body><span>good</span></body></html>'
Selector(text=body).xpath('//span/text()').get()

通过response参数 初始化

from scrapy.selector import Selector
from scrapy.http import HtmlResponse
response = HtmlResponse(url='http://example.com', body=body)
Selector(response=response).xpath('//span/text()').get()
'good'

选择器的方法

S.N.方法 & 描述
extract()、getall()它返回一个unicode字符串以及所选数据
extract_first()、get()它返回第一个unicode字符串以及所选数据
re()它返回Unicode字符串列表,当正则表达式被赋予作为参数时提取
xpath()它返回选择器列表,它代表由指定XPath表达式参数选择的节点
css()它返回选择器列表,它代表由指定CSS表达式作为参数所选择的节点

Scrapy_ScrapyShell的使用 

Scrapy Shell是一个交互式shell,可以在不运行spider项目时,快速调试 scrapy 代码。

提示:一般用于测试xpath或css表达式,查看它们是否能提取想要的数据

注意:当从命令行运行Scrapy Shell时,记住总是用引号括住url,否则url包含参数(即 & 字符)不起作用。在Windows上,使用双引号:

scrapy shell "https://scrapy.org"

Scrapy_保存数据到文件 

保存数据到文件有两种方法:

方法1:使用python原生方法保存

with open("movie.txt", 'wb') as f:
  for n, c in zip(movie_name, movie_core):
    str = n+":"+c+"\n"
    f.write(str.encode())

方法2:使用Scrapy内置方法

scrapy 内置主要有四种:JSON,JSON lines,CSV,XML
最常用的导出结果格为JSON,命令如下:

scrapy crawl dmoz -o douban.json -t json 

参数设置:
        -o 后面导出文件名
        -t 后面导出的类型,可以省略,但要保存的文件名后缀,写清楚类型

注意:将数据解析完,返回数据,才可以用命令保存,代码如下,格式为dict或item类型

  • return data
  • yield data

Scrapy_ItemPipeline的使用

数据在Spider中被收集之后,可以传递Item Pipeline中统一进行处理

特点:每个item pipeline就是一个普通的python类,包含的方法名称如下:

方法名含义是否必须实现
process_item(self,item,spider)用于处理接收到的item
open_spider(self,spider)表示当spider被开启的时候调用这个方法
close_spider(self,spider)当spider关闭时候这个方法被调用

功能
        1.接收item:在process_item方法中保存
        2.是否要保存数据:取决于是否编写代码用于保存数据
        3.决定此Item是否进入下一个pipeline
                return item 数据进入下一个pipeline
                drop item 抛弃数据 

代码示例:

class SaveFilePipeline:
  def open_spider(self,spider):
    self.file = open('douban5.txt','w')
  def process_item(self, item, spider):
    self.file.write(f'name:{item.get("name")}  score:{item.get("score")}\n')
  def close_spider(self,spider):
    self.file.close()
from scrapy.exceptions import DropItem
class XSPipeline:
  def open_spider(self,spider):
    self.file = open('xs.txt','w',encoding='utf-8')


  def process_item(self, item, spider):
    if item.get('title'):
      self.file.write(item.get('title'))
      self.file.write('\n')
      return item
    else:
      raise DropItem(f"Missing title in {item}")
  
  def close_spider(self,spider):
    self.file.close()

Scrapy_ImagePipeline保存图片

Scrapy提供了一个 ImagePipeline,用来下载图片这条管道,图片管道ImagesPipeline 提供了方便并具有额外特性的功能,比如:

  • 将所有下载的图片转换成通用的格式(JPG)和模式(RGB)
  • 避免重新下载最近已经下载过的图片
  • 缩略图生成
  • 检测图像的宽/高,确保它们满足最小限制

使用图片管道:

scrapy.pipelines.images.ImagesPipeline

使用 ImagesPipeline ,典型的工作流程如下所示:

  1. 在一个爬虫中,把图片的URL放入 image_urls 组内(image_urls是个列表)
  2. URL从爬虫内返回,进入图片管道
  3. 当图片对象进入 ImagesPipeline,image_urls 组内的URLs将被Scrapy的调度器和下载器安排下载
  4. settings.py文件中配置保存图片路径参数IMAGES_STORE
  5. 开启管道

Scrapy_自定义ImagePipeline

自定义图片管道

  • 继承scrapy.pipelines.images import ImagesPipeline

  • 实现get_media_requests(self, item, info)方法

    • 发送请求,下载图片
    • 转发文件名
  • 实现file_path(self,request,response=None,info=None,*,item=None)

    • 修改文件名与保存路径

代码示例:

import re


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


from scrapy.pipelines.images import ImagesPipeline
from scrapy.http.request import Request


class MyImagePipeline(ImagesPipeline):
  def get_media_requests(self, item, info):
    return Request(item['image_url'])
  
  def file_path(self, request, response=None, info=None, *, item=None):
    # 处理文件名中的特殊字符
    # name = item.get('name').strip().replace('\r\n\t\t','').replace('(','').replace(')','').replace('/','_')
    name = re.sub('/','_',re.sub('[\s()]','',item.get('name')))
    return f'{name}.jpg'

Scrapy_配置文件Settings的使用

Scrapy允许自定义设置所有Scrapy组件的行为,包括核心、扩展、管道和spider本身。

官网配置文档:官网-参考配置

配置文档

  • BOT_NAME

    默认: 'scrapybot'

    Scrapy项目实现的bot的名字。用来构造默认 User-Agent,同时也用来log。 当你使用 startproject 命令创建项目时其也被自动赋值。

  • CONCURRENT_ITEMS

    默认: 100

    Item Processor(即 Item Pipeline) 同时处理(每个response的)item的最大值

  • CONCURRENT_REQUESTS

    默认: 16

    Scrapy downloader 并发请求(concurrent requests)的最大值。

  • CONCURRENT_REQUESTS_PER_DOMAIN

    默认: 8

    对单个网站进行并发请求的最大值。

  • CONCURRENT_REQUESTS_PER_IP

    默认: 0

    对单个IP进行并发请求的最大值。如果非0,则忽略 CONCURRENT_REQUESTS_PER_DOMAIN 设定, 使用该设定。 也就是说,并发限制将针对IP,而不是网站。

    该设定也影响 DOWNLOAD_DELAY: 如果 CONCURRENT_REQUESTS_PER_IP 非0,下载延迟应用在IP而不是网站上。

  • FEED_EXPORT_ENCODING ='utf-8'

    设置导出时文件的编码

  • DEFAULT_REQUEST_HEADERS

    默认:

    {
      'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
      'Accept-Language': 'en',
    }
    

    Scrapy HTTP Request使用的默认header。由 DefaultHeadersMiddleware 产生。

  • DOWNLOADER_MIDDLEWARES

    默认:: {}

    保存项目中启用的下载中间件及其顺序的字典

  • DOWNLOAD_DELAY

    默认: 0

    下载器在下载同一个网站下一个页面前需要等待的时间。该选项可以用来限制爬取速度, 减轻服务器压力。同时也支持小数

  • DOWNLOAD_TIMEOUT

    默认: 180

    下载器超时时间(单位: 秒)

  • ITEM_PIPELINES

    默认: {}

    保存项目中启用的pipeline及其顺序的字典。该字典默认为空,值(value)任意。 不过值(value)习惯设定在0-1000范围内

  • DEPTH_LIMIT

    默认:0

    类:scrapy.spidermiddlewares.depth.DepthMiddleware

    允许为任何站点爬行的最大深度。如果为零,则不会施加任何限制。

  • LOG_ENABLED

    默认: True

    是否启用logging

  • LOG_ENCODING

    默认: 'utf-8'

    logging使用的编码。

  • LOG_FILE

    默认: None

    logging输出的文件名。如果为None,则使用标准错误输出(standard error)。

  • LOG_FORMAT

    默认: '%(asctime)s [%(name)s] %(levelname)s: %(message)s'

    日志的数据格式

  • LOG_DATEFORMAT

    默认: '%Y-%m-%d %H:%M:%S'

    日志的日期格式

  • LOG_LEVEL

    默认: 'DEBUG'

    log的最低级别。可选的级别有: CRITICAL、 ERROR、WARNING、INFO、DEBUG

  • LOG_STDOUT

    默认: False

    如果为 True ,进程所有的标准输出(及错误)将会被重定向到log中

  • ROBOTSTXT_OBEY

    默认: True

    是否遵循robots协议

  • USER_AGENT

    默认: "Scrapy/VERSION (+http://scrapy.org)"

    爬取的默认User-Agent,除非被覆盖

Scrapy默认BASE设置

scrapy对某些内部组件进行了默认设置,这些组件通常情况下是不能被修改的,但是我们在自定义了某些组件以后,比如我们设置了自定义的middleware中间件,需要按照一定的顺序把他添加到组件之中,这个时候需要参考scrapy的默认设置,因为这个顺序会影响scrapy的执行,下面列出了scrapy的默认基础设置

注意

如果想要修改以下的某些设置,应该避免直接修改下列内容,而是修改其对应的自定义内容

{
  'scrapy.downloadermiddlewares.robotstxt.RobotsTxtMiddleware': 100,
  'scrapy.downloadermiddlewares.httpauth.HttpAuthMiddleware': 300,
  'scrapy.downloadermiddlewares.downloadtimeout.DownloadTimeoutMiddleware': 350,
  'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': 400,
  'scrapy.downloadermiddlewares.retry.RetryMiddleware': 500,
  'scrapy.downloadermiddlewares.defaultheaders.DefaultHeadersMiddleware': 550,
  'scrapy.downloadermiddlewares.redirect.MetaRefreshMiddleware': 580,
  'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware': 590,
  'scrapy.downloadermiddlewares.redirect.RedirectMiddleware': 600,
  'scrapy.downloadermiddlewares.cookies.CookiesMiddleware': 700,
  'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware': 750,
  'scrapy.downloadermiddlewares.chunked.ChunkedTransferMiddleware': 830,
  'scrapy.downloadermiddlewares.stats.DownloaderStats': 850,
  'scrapy.downloadermiddlewares.httpcache.HttpCacheMiddleware': 900,
}

如果需要关闭下载处理器,为其赋值为 None 即可

提示

有时添加了一些自定义的组件,无法应用到效果,可以从执行顺序方面入手

执行顺序:输值越小,优先级越高

 Scrapy_爬取小说网数据_案例练习

spider代码:

import scrapy


class XiaoshuoSpiderSpider(scrapy.Spider):
  name = 'xiaoshuo_spider'
  allowed_domains = ['zy200.com']
  url = 'http://www.zy200.com/5/5943/'
  start_urls = [url + '11667352.html']


  def parse(self, response):
    info = response.xpath("/html/body/div[@id='content']/text()").extract()
    href = response.xpath("//div[@class='zfootbar']/a[3]/@href").extract_first()


    yield {'content':info}


    if href != 'index.html':
      new_url = self.url + href
      yield scrapy.Request(new_url, callback=self.parse)

 pipeline代码:
 

class XiaoshuoPipeline(object):
  def __init__(self):
    self.filename = open("dp1.txt", "w", encoding="utf-8")


  def process_item(self, item, spider):
    content = item["title"] + item["content"] + '\n'
    self.filename.write(content)
    self.filename.flush()
    return item


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

Scrapy_CrawlSpider的介绍

在Scrapy中Spider是所有爬虫的基类,而CrawSpiders就是Spider的派生类。
适用于先爬取start_url列表中的网页,再从爬取的网页中获取link并继续爬取的工作。运行图如下

创建CrawlSpider:

scrapy genspider -t crawl 爬虫名 (allowed_url)

Rule对象:Rule类与CrawlSpider类都位于scrapy.contrib.spiders模块中

class scrapy.contrib.spiders.Rule( 
    link_extractor,         
    callback=None,
    cb_kwargs=None,
    follow=None,
    process_links=None,
    process_request=None) 

参数含义:

  • link_extractor为LinkExtractor,用于定义需要提取的链接

  • callback参数:当link_extractor获取到链接时参数所指定的值作为回调函数

    注意 回调函数尽量不要用parse方法,crawlspider已使用了parse方法

  • follow:指定了根据该规则从response提取的链接是否需要跟进。当callback为None,默认值为True

  • process_links:主要用来过滤由link_extractor获取到的链接

  • process_request:主要用来过滤在rule中提取到的request

LinkExtractors:链接提取器

作用:response对象中获取链接,并且该链接会被接下来爬取 每个LinkExtractor有唯一的公共方法是 extract_links(),它接收一个 Response 对象,并返回一个 scrapy.link.Link 对象

class scrapy.linkextractors.LinkExtractor(
  allow = (),
  deny = (),
  allow_domains = (),
  deny_domains = (),
  deny_extensions = None,
  restrict_xpaths = (),
  tags = ('a','area'),
  attrs = ('href'),
  canonicalize = True,
  unique = True,
  process_value = None
)

主要参数:

  • allow:满足括号中“正则表达式”的值会被提取,如果为空,则全部匹配。
  • deny:与这个正则表达式(或正则表达式列表)不匹配的URL一定不提取。
  • allow_domains:会被提取的链接的domains。
  • deny_domains:一定不会被提取链接的domains。
  • restrict_xpaths:使用xpath表达式,和allow共同作用过滤链接(只选到节点,不选到属性)
  • restrict_css:使用css表达式,和allow共同作用过滤链接(只选到节点,不选到属性)

 查看效果-shell中验证

首先运行:

scrapy shell 'https://www.zhhbqg.com/1_1852/835564.html'

继续import相关模块:

from scrapy.linkextractors import LinkExtractor

提取当前网页中获得的链接:

link = LinkExtractor(restrict_xpaths=(r'//a'))

调用LinkExtractor实例的extract_links()方法查询匹配结果:

 link.extract_links(response)

查看效果 CrawlSpider版本

from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from xiaoshuo.items import XiaoshuoItem

class XiaoshuoSpiderSpider(CrawlSpider):
  name = 'xiaoshuo_spider'
  allowed_domains = ['fhxiaoshuo.com']
  start_urls = ['http://www.fhxiaoshuo.com/read/33/33539/17829387.shtml']

  rules = [
    Rule(LinkExtractor(restrict_xpaths=(r'//div[@class="bottem"]/a[4]')), callback='parse_item'),]

  def parse_item(self, response):
    info = response.xpath("//div[@id='TXT']/text()").extract()
    it = XiaoshuoItem()
    it['info'] = info
    yield it


注意

  • callback后面函数名用引号引起
  • 函数名不要用parse
  • 参数的括号嵌套,不要出问题

Scrapy_Request对象的介绍 

爬虫中请求与响应是最常见的操作,Request对象在爬虫程序中生成并传递到下载器中,后者执行请求返回一个Response对象

Request对象:

class scrapy.http.Request(url[, callback, method='GET', headers, body, cookies, meta, encoding='utf-8', priority=0, dont_filter=False, errback])

一个Request对象表示一个HTTP请求,它通常是在爬虫生成,并由下载执行,从而生成Response

参数

  • url(string) - 此请求的网址

  • callback(callable) - 将使用此请求的响应(一旦下载)作为其第一个参数调用的函数。有关更多信息,请参阅下面的将附加数据传递给回调函数。如果请求没有指定回调,parse()将使用spider的 方法。请注意,如果在处理期间引发异常,则会调用errback。

  • method(string) - 此请求的HTTP方法。默认为'GET'。可设置为"GET", "POST", "PUT"等,且保证字符串大写

  • meta(dict) - 属性的初始值Request.meta,在不同的请求之间传递数据使用

  • body(str或unicode) - 请求体。如果unicode传递了,那么它被编码为 str使用传递的编码(默认为utf-8)。如果 body没有给出,则存储一个空字符串。不管这个参数的类型,存储的最终值将是一个str(不会是unicode或None)。

  • headers(dict) - 这个请求的头。dict值可以是字符串(对于单值标头)或列表(对于多值标头)。如果 None作为值传递,则不会发送HTTP头.一般不需要

  • encoding: 使用默认的 'utf-8' 就行

  • dont_filter:是否过滤重复的URL地址,默认为 False过滤

  • cookie(dict或list) - 请求cookie。这些可以以两种形式发送。

    • 使用dict

    • request_with_cookies = Request(url="http://www.sxt.cn/index/login/login.html",)
      
    • 使用列表

    •  request_with_cookies = Request(url="http://www.example.com",
                      cookies=[{'name': 'currency',
                          'value': 'USD',
                          'domain': 'example.com',
                          'path': '/currency'}])
      

      后一种形式允许定制 cookie的属性domain和path属性。这只有在保存Cookie用于以后的请求时才有用

    • request_with_cookies = Request(url="http://www.example.com",
                      cookies={'currency': 'USD', 'country': 'UY'},
                      meta={'dont_merge_cookies': True})
      

将附加数据传递给回调函数

请求的回调是当下载该请求的响应时将被调用的函数。将使用下载的Response对象作为其第一个参数来调用回调函数

def parse_page1(self, response):
  item = MyItem()
  item['main_url'] = response.url
  request = scrapy.Request("http://www.example.com/some_page.html",
               callback=self.parse_page2)
  request.meta['item'] = item
  return request


def parse_page2(self, response):
  item = response.meta['item']
  item['other_url'] = response.url
  return item

Scarpy中FormRequest对象的使用

FormRequest是Request的扩展类,具体常用的功能如下:请求时,携带参数,如表单数据
或者从Response中获取表单的数据,formdata参数类型为:dict

FormRequest类可以携带参数主要原因是:增加了新的构造函数的参数formdata。其余的参数与Request类相同.

class scrapy.http.FormRequest(url[, formdata, ...])
class method from_response(response[, formname=None, formid=None, formnumber=0, formdata=None, formxpath=None, formcss=None, clickdata=None, dont_click=False, ...])

返回一个新FormRequest对象,其中的表单字段值已预先<form>填充在给定响应中包含的HTML 元素中.

参数:

  • response(Responseobject) - 包含将用于预填充表单字段的HTML表单的响应
  • formname(string) - 如果给定,将使用name属性设置为此值的形式
  • formid(string) - 如果给定,将使用id属性设置为此值的形式
  • formxpath(string) - 如果给定,将使用匹配xpath的第一个表单
  • formcss(string) - 如果给定,将使用匹配css选择器的第一个形式
  • formnumber(integer) - 当响应包含多个表单时要使用的表单的数量。第一个(也是默认)是0
  • formdata(dict) - 要在表单数据中覆盖的字段。如果响应元素中已存在字段,则其值将被在此参数中传递的值覆盖
  • clickdata(dict) - 查找控件被点击的属性。如果没有提供,表单数据将被提交,模拟第一个可点击元素的点击。除了html属性,控件可以通过其相对于表单中其他提交表输入的基于零的索引,通过nr属性来标识
  • dont_click(boolean) - 如果为True,表单数据将在不点击任何元素的情况下提交

通过HTTP POST发送数据 

FormRequest(
           url="http://www.example.com/post/action",
      formdata={'name': 'John Doe', 'age': '27'},
      callback=self.after_post
      )

通过FormRequest.from_response()发送数据 

FormRequest.from_response(
      response,
      formdata={'username': 'john', 'password': 'secret'},
      callback=self.after_login
)

响应对象 

class scrapy.http.Response(url[, status=200, headers=None, body=b'', flags=None, request=None])

一个Response对象表示的HTTP响应,这通常是下载器下载后,并供给到爬虫进行处理

参数:

  • url(string) - 此响应的URL
  • status(integer) - 响应的HTTP状态。默认为200
  • headers(dict) - 这个响应的头。dict值可以是字符串(对于单值标头)或列表(对于多值标头)
  • body(bytes) - 响应体。它必须是str,而不是unicode,除非你使用一个编码感知响应子类,如 TextResponse
  • flags(list) - 是一个包含属性初始值的 Response.flags列表。如果给定,列表将被浅复制
  • request(Requestobject) - 属性的初始值Response.request。这代表Request生成此响应
  • text 获取文本

Scrapy_下载中间件介绍

下载中间件是Scrapy请求/响应处理的钩子框架。这是一个轻、低层次的应用。

通过可下载中间件,可以处理请求之前请求之后的数据。

每个中间件组件都是一个Python类,它定义了一个或多个以下方法,我们可能需要使用方法如下:

process_request()

process_response()

process_request(self, request, spider) 

当每个request通过下载中间件时,该方法被调用

必须返回以下其中之一

  • 返回 None

    • Scrapy 将继续处理该 request,执行其他的中间件的相应方法,直到合适的下载器处理函数(download handler)被调用,该 request 被执行(其 response 被下载)
  • 返回一个 Response 对象

    • Scrapy 将不会调用 任何 其他的 process_request()或 process_exception()方法,或相应地下载函数; 其将返回该 response。已安装的中间件的 process_response()方法则会在每个 response 返回时被调用
  • 返回一个 Request 对象

    • Scrapy 则停止调用 process_request 方法并重新调度返回的 request。当新返回的 request 被执行后, 相应地中间件链将会根据下载的 response 被调用
  • raise IgnoreRequest

    • 如果抛出 一个 IgnoreRequest 异常,则安装的下载中间件的 process_exception() 方法会被调用。如果没有任何一个方法处理该异常, 则 request 的 errback(Request.errback)方法会被调用。如果没有代码处理抛出的异常, 则该异常被忽略且不记录(不同于其他异常那样)

参数:

  • request (Request 对象) – 处理的request
  • spider (Spider 对象) – 该request对应的spider

process_response(self, request, response, spider)

当下载器完成http请求,传递响应给引擎的时候调用

process_response()应该是:返回一个 Response对象,则返回一个 Request 对象或引发 IgnoreRequest例外情况。

  • 如果它返回 Response(可能是相同的给定响应,也可能是全新的响应),该响应将继续使用 process_response() 链中的下一个中间件

  • 如果它返回一个 Request 对象时,中间件链将暂停,返回的请求将重新计划为将来下载。这与从返回请求的行为相同 process_request()

  • 如果它引发了 IgnoreRequest异常,请求的errback函数 (Request.errback )。如果没有代码处理引发的异常,则忽略该异常,不记录该异常(与其他异常不同)。

  • 参数

    • request (is a Request object) -- 发起响应的请求
    • response (Responseobject) -- 正在处理的响应
    • spider (Spider object) -- 此响应所针对的蜘蛛

Scrapy_下载中间件设置UserAgent

下载中间件是Scrapy请求/响应处理的钩子框架。这是一个轻、低层次的应用。

通过可下载中间件,可以处理请求之前请求之后的数据。

如果使用下载中间件需要在Scrapy中的setting.py的配置DOWNLOADER_MIDDLEWARES才可以使用,

比如:

DOWNLOADER_MIDDLEWARES = {
  'myproject.middlewares.CustomDownloaderMiddleware': 543,
}

开发UserAgent下载中间件

问题

每次创建项目后,需要自己复制UserAgent到settings,比较繁琐


解决方案

开发下载中间件,设置UserAgent

代码

from fake_useragent import UserAgent


class MyUserAgentMiddleware:
  def process_request(self, request, spider):
    request.headers.setdefault(b'User-Agent', UserAgent().chrome)

三方模块:

pip install scrapy-fake-useragent==1.4.4

 配置模块到Setting文件:

DOWNLOADER_MIDDLEWARES = {
  'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': None,
  'scrapy.downloadermiddlewares.retry.RetryMiddleware': None,
  'scrapy_fake_useragent.middleware.RandomUserAgentMiddleware': 400,
  'scrapy_fake_useragent.middleware.RetryUserAgentMiddleware': 401,
}

 Scrapy_下载中间件设置代理

爬虫设置代理就是让别的服务器或电脑代替自己的服务器去获取数据

通过request.meta['proxy']可以设置代理,如下:

class MyProxyDownloaderMiddleware:
  def process_request(self, request, spider):
    # request.meta['proxy'] ='http://ip:port'
    # request.meta['proxy'] ='http://name:pwd@ip:port'
    request.meta['proxy'] ='http://139.224.211.212:8080'


 Scrapy_下载中间件结合selenium使用

有的页面反爬技术比较高端,一时破解不了,这时我们就是可以考虑使用selenium来降低爬取的难度。

问题来了,如何将Scrapy与Selenium结合使用呢?

思考的思路: 只是用Selenium来帮助下载数据。因此可以考虑通过下载中间件来处理这块内容。

    Spider文件

    @classmethod
     def from_crawler(cls, crawler, *args, **kwargs):
        spider = super(BaiduSpider, cls).from_crawler(crawler, *args, **kwargs)
        spider.chrome = webdriver.Chrome(executable_path='../tools/chromedriver.exe')
        crawler.signals.connect(spider.spider_closed, signal=signals.spider_closed) 
        # connect里的参数 
        # 1. 处罚事件后用哪个函数处理
        # 2. 捕捉哪个事件
        return spider
    
      def spider_closed(self, spider):
        spider.chrome.close()
    

     middlewares文件:

    def process_request(self, request, spider): 
        spider.chrome.get(request.url)
        html = spider.chrome.page_source
        return HtmlResponse(url = request.url,body = html,request = request,encoding='utf-8')
    

    您可能感兴趣的与本文相关的镜像

    Python3.8

    Python3.8

    Conda
    Python

    Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

    评论
    成就一亿技术人!
    拼手气红包6.0元
    还能输入1000个字符
     
    红包 添加红包
    表情包 插入表情
     条评论被折叠 查看
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包

    打赏作者

    ST_小罗

    你的鼓励将是我创作的最大动力

    ¥1 ¥2 ¥4 ¥6 ¥10 ¥20
    扫码支付:¥1
    获取中
    扫码支付

    您的余额不足,请更换扫码支付或充值

    打赏作者

    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

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

    余额充值