📜  如何使用 Scrapy 下载文件?

📅  最后修改于: 2022-05-13 01:55:07.645000             🧑  作者: Mango

如何使用 Scrapy 下载文件?

Scrapy 是一个快速的高级网页抓取和网页抓取框架,用于抓取网站并从其页面中提取结构化数据。它可用于广泛的用途,从数据挖掘到监控和自动化测试。在本教程中,我们将探索如何使用scrapy crawl spider 下载文件。

对于初学者来说,网络爬虫是一种遍历万维网以下载与特定主题相关的信息的方法。要记住的一件事是,并非所有网站都允许您抓取他们的页面,因此在尝试抓取页面之前参考他们的 robots.txt 文件始终是一个好习惯。

第 1 步:安装软件包:

在我们开始编码之前,我们需要安装 Scrapy 包

pip install scrapy

第 2 步:创建项目



# scrapyProject is the name we chose for 
# the folder that will contain the project
mkdir scrapyProject
cd scrapyProject

# downFiles is the name of the project
scrapy startproject downFiles

在终端中运行上述代码后的输出如下:

开始一个新的scrapy项目的输出

第 3 步:选择蜘蛛模板

Scrapy 自带 4 个蜘蛛模板,分别是:

  1. 基础:通用
  2. crawl: 用于爬行,或跟随链接(下载文件首选)
  3. csvfeed: 用于解析 CSV 文件
  4. xmlfeed:用于解析 XML 文件

在本教程中,我们将使用爬网蜘蛛模板并在此基础上进一步构建。

在scrapy中查看可用的蜘蛛模板:

scrapy genspider -l

在scrapy中可用的4个蜘蛛模板

在我们开始构建蜘蛛的基本结构之前,请确保您在步骤 2 中创建的项目目录(包含 spider.cfg 文件的目录)中工作

要更改您的目录

# the project name we had decided was 
# downFiles in step2
cd downFiles 

创建爬行蜘蛛的基本结构:

将使用以下内容创建一个带有蜘蛛名称的新Python文件:

该文件将位于

在哪里

  • scrapyProject 是包含项目的目录名
  • downFiles 是项目名称
  • nirsoft.py 是新创建的“空”蜘蛛

代码:

Python3
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
  
  
class NirsoftSpider(CrawlSpider):
    name = 'nirsoft'
    allowed_domains = ['www.nirsoft.net']
    start_urls = ['http://www.nirsoft.net/']
  
    rules = (
        Rule(LinkExtractor(allow=r'Items/'),
             callback='parse_item', follow=True),
    )
  
    def parse_item(self, response):
        item = {}
        return item


Python3
rules = (
    Rule(LinkExtractor(allow = r'Items/'),
         callback = 'parse_item',
         follow = True),
)


Python3
rules = (
    Rule(LinkExtractor(allow=r'utils/'),
         callback='parse_item', follow = True),
)


Python3
def parse_item(self, response):
    file_url = response.css('.downloadline::attr(href)').get()
    file_url = response.urljoin(file_url)
    yield {'file_url': file_url}


Python3
class DownfilesItem(scrapy.Item):
    
    # define the fields for your item here like:
    file_urls = scrapy.Field()
    files = scrapy.Field


Python3
def parse_item(self, response):
    file_url = response.css('.downloadline::attr(href)').get()
    file_url = response.urljoin(file_url)
    item = DownfilesItem()
    item['file_urls'] = [file_url]
    yield item


Python3
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from downFiles.items import DownfilesItem


Python3
def parse_item(self, response):
    file_url = response.css('.downloadline::attr(href)').get()
    file_url = response.urljoin(file_url)
    file_extension = file_url.split('.')[-1]
    if file_extension not in ('zip', 'exe', 'msi'):
        return
    item = DownfilesItem()
    item['file_urls'] = [file_url]
    item['original_file_name'] = file_url.split('/')[-1]
    yield item


Python3
class DownfilesItem(scrapy.Item):
    # define the fields for your item here like:
    file_urls = scrapy.Field()
    original_file_name = scrapy.Field()
    files = scrapy.Field


Python3
from scrapy.pipelines.files import FilesPipeline
  
  
class DownfilesPipeline(FilesPipeline):
    def file_path(self, request, response=None, info=None):
        file_name: str = request.url.split("/")[-1]
        return file_name


这是一个“空”的爬虫。执行时不会产生任何结果。为了提取信息,我们需要告诉蜘蛛它需要爬取哪些链接。

注意:这就是 Scrapy 与其他流行的网络爬行包(如Selenium)不同的地方, Selenium如果未指定,则爬取所有数据(即使不必要)。此功能使 Scrapy 比Selenium更快。

第 4 步:定义链接提取规则

蟒蛇3



rules = (
    Rule(LinkExtractor(allow = r'Items/'),
         callback = 'parse_item',
         follow = True),
)

上面的代码段是用来处理蜘蛛将要爬取的链接的。可以使用多个命令来制定规则,但在本教程中,我们将仅使用少数几个常用命令。我们将尝试下载 nirsoft.net 提供的一些工具 所有工具或实用程序都在其实用程序下可用,因此所有相关链接都遵循给定的模式:

https://www.nirsoft.net/utils/...

所以上面的代码段将被编辑如下:

蟒蛇3

rules = (
    Rule(LinkExtractor(allow=r'utils/'),
         callback='parse_item', follow = True),
)

第四步:解析抓取到的页面

现在我们已经设置了要抓取的链接,接下来我们需要定义蜘蛛应该抓取的内容。为此,我们将不得不检查相关页面。前往上述任何示例并打开检查元素模式(对于 Windows,ctrl+shift+c,对于 MacOS,cmd+shift+c)



a.downloadline 显示所有的下载链接都是类名“downloadline”下的锚标签

  • 我们可以看到下载链接都是一个锚标签(a),类名是“downloadline”(a.downloadline)
  • 所以现在我们将在 CSS 选择器中使用它并提取锚标记的 href 属性
  • 为了让爬虫高效工作,我们还需要将相对链接转换为绝对链接。幸运的是,最新版本的 Scrapy 使我们能够通过一个简单的方法来做到这一点: urljoin()

因此 parse_item() 方法将如下所示:

蟒蛇3

def parse_item(self, response):
    file_url = response.css('.downloadline::attr(href)').get()
    file_url = response.urljoin(file_url)
    yield {'file_url': file_url}

如果我们在此状态下运行爬虫,我们将获得 nirsoft 中所有可用实用程序的链接。

scrapy crawl nirsoft

对于初学者:我建议现在不要运行上面的命令,因为你的命令提示符将充斥着大量的 URL,这些 URL 滚动得太快,你的眼睛无法感知任何东西。

在几秒钟内,我们的命令行将充斥着所有抓取的 URL

相反,让我们继续下一步

步骤 5:下载文件

最后,我们都在等待的那一刻,下载文件。但是,在我们开始之前,我们需要编辑最初创建蜘蛛时创建的项目类。该文件可以在以下位置找到:

...\scrapyProject\downFiles\downFiles\items.py

在哪里

  • scrapyProject 是包含项目的目录名
  • downFiles 是项目名称
  • items.py 是相关项目的类

项目类必须编辑如下:

蟒蛇3



class DownfilesItem(scrapy.Item):
    
    # define the fields for your item here like:
    file_urls = scrapy.Field()
    files = scrapy.Field

现在我们更新蜘蛛脚本以使用我们定义的数据字段

蟒蛇3

def parse_item(self, response):
    file_url = response.css('.downloadline::attr(href)').get()
    file_url = response.urljoin(file_url)
    item = DownfilesItem()
    item['file_urls'] = [file_url]
    yield item

您还必须将之前定义的项目类导入到蜘蛛脚本中,因此蜘蛛脚本的导入部分将如下所示,

蟒蛇3

import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from downFiles.items import DownfilesItem

最后,要启用文件下载,我们需要对项目目录中的settings.py文件进行两个小的更改:

1. 启用文件下载:

ITEM_PIPELINES = {
  'scrapy.pipelines.files.FilesPipeline': 1,
}

2. 在 settings.py 中指定下载的目标文件夹:

FILES_STORE = r"D:\scrapyProject\nirsoft\downloads"

注意:文件夹目的地应为实际目的地

我们使用原始字符串来避免由于 Windows 位置字符串的反斜杠引起的任何错误

现在如果我们运行

scrapy crawl nirsoft

我们将能够找到下载到指定目标文件夹的所有文件,这样我们就完成了!

限制要下载的文件类型

由于我们旨在下载实用程序的安装文件,因此最好将爬虫限制为仅下载 .zip 和 .exe 文件,而将其余文件排除在外。这也将减少抓取时间,从而使脚本更高效。



为此,我们需要编辑我们的 parse_items() 函数,如下所示:

蟒蛇3

def parse_item(self, response):
    file_url = response.css('.downloadline::attr(href)').get()
    file_url = response.urljoin(file_url)
    file_extension = file_url.split('.')[-1]
    if file_extension not in ('zip', 'exe', 'msi'):
        return
    item = DownfilesItem()
    item['file_urls'] = [file_url]
    item['original_file_name'] = file_url.split('/')[-1]
    yield item

我们还需要将新的数据字段“original_file_name”添加到我们的项目类定义中:

蟒蛇3

class DownfilesItem(scrapy.Item):
    # define the fields for your item here like:
    file_urls = scrapy.Field()
    original_file_name = scrapy.Field()
    files = scrapy.Field

保存所有更改并运行,

scrapy crawl nirsoft

我们将能够找到下载到指定目标文件夹的所有 .zip 和 .exe 文件。但是,我们仍然有一个问题:

SHA1 哈希码不是人类可读的,因此最好使用它们的原始(人类可读)名称保存文件,这将引导我们进入下一部分

创建自定义管道

最初,我们使用 Scrapy 的默认管道来下载文件,但是,问题是文件使用其 SHA1 哈希码而不是人类可读的文件名保存。因此,我们需要创建一个自定义管道来保存原始文件名,然后在下载文件时使用该名称。

就像我们的项目类 (items.py) 一样,我们也有一个管道类 (pipelines.py),其中包含在我们创建项目时为我们的项目生成的类,我们将使用这个类来创建我们的自定义管道



蟒蛇3

from scrapy.pipelines.files import FilesPipeline
  
  
class DownfilesPipeline(FilesPipeline):
    def file_path(self, request, response=None, info=None):
        file_name: str = request.url.split("/")[-1]
        return file_name

我们导入了 Scrapy 提供的默认 FilesPipeline,然后覆盖了 file_path函数,这样它就不会使用哈希码作为文件名,而是使用文件名。

我们注释掉了 process_item函数,以便它不会覆盖 FilesPipeline 中的默认 process_item函数。

接下来,我们更新我们的 settings.py 文件以使用我们的自定义管道而不是默认管道。

ITEM_PIPELINES = {
  'downFiles.pipelines.DownfilesPipeline': 1,
}

最后,我们运行

scrapy crawl nirsoft

我们有我们的结果:

由于自定义管道,下载的文件更具可读性