006:开启Scrapy爬虫项目之旅

2021-11-22 14:50:30 浏览数 (1)

上一篇文章介绍了Scrapy框架的安装及其目录结构和常用工具命令,相信大家也有了初步的认识。 本章将从实战编写来补充scrapy的基础知识

Items的编写:

使用Scrapy中的Item对象可以保存爬取到的数据,相当于存储爬取到数据的容器。 我们可以定义自己所关注的结构化信息,然后从庞大的互联网信息体系中提取出我们关注度的结构化信息,这样可以更利于我们对数据的管理,提取之后,这些数据信息需要一个储存的地方,可以将提取到的结构化数据储存到Item对象中。 说白了就是我们需要什么就写什么,比如网页标题,网页关键词,网页地址等。 现在我们打开之前的 First_Get 爬虫项目,打开里面的items.py,打开后内容是这样的:

我们要对结构化信息进行定义,可以直接修改对应的类,比如此时需要修改的类为FirstGetItem 定义结构化数据信息的格式如下:

结构化数据名 = scrapy.Field() 所以,若是要对结构化数据网页标题、网页关键词、网页版权信息、网页地址等进行定义,可以修该为如下:

所以我们要定义一个结构化数据,只需要将scrapy下的Field类实例化即可。 完成之后我们可以通过python shell命令行来实际使用一下Items,更深入的理解Items。 首先我们需要打开python shell,(可以直接使用IDLE的shell界面)。 先导入scrapy 模块,然后继承一个scrapy.Item的类。开始定义我们要存储的结构化数据。如下所示

代码语言:javascript复制
import scrapy
class Person(scrapy.Item):
	name = scrapy.Field()
	job = scrapy.Field()
	email =scrapy.Field()

我们定义了人的结构化数据之后,只需要实例化该类, xixixi=Person(name = “lx”,job=“teacher”,email="lx@qq.com")

实例化之后,输入xixixi即可: 结果为:

可以发现,对应的数据会以字典的形式储存,原数据会转变为字典中的字段名,原数据项对应的值会转变为字典中响应字段名对应的值,比如原来的name=‘lx’,会变为’name’:‘lx’

Spider的编写:

Spider类是Scrapy中与爬虫相关的一个基类,所有的爬虫文件必须继承该类。 在一个爬虫项目中,爬虫文件是一个及其重要的部分,爬虫所进行的爬取动作以及数据提取等操作都是在该文件中进行定义和编写的。 比如我们可以在爬虫项目中通过genspider命令创建一个爬虫文件,然后对该文件进行相应的编写和修改。

打开我们之前的xixixi.py

可以看到他导入了scrapy模块,然后创建了一个爬虫类,该类继承了scrapy.Spider基类。 name属性代表的是爬虫名称。 allowed_domains代表的是允许爬行的域名。 start_urls属性代表的是爬行的起始网址。

下面是一些spider常用的属性和方法含义: 属性: name:spider的名称,要求唯一 allowed_domains:允许的域名,限制爬虫的范围 start_urls:初始urls custom_settings:个性化设置,会覆盖全局的设置 crawler:抓取器,spider将绑定到它上面 custom_settings:配置实例,包含工程中所有的配置变量 logger:日志实例,打印调试信息 方法: from_crawler(crawler, *args, **kwargs):类方法,用于创建spider start_requests():生成初始的requests make_requests_from_url(url):遍历urls,生成一个个request parse(response):用来解析网页内容 log(message[,level.component]):用来记录日志,这里请使用logger属性记录日志,self.logger.info(‘visited success’) closed(reason):当spider关闭时调用的方法

我们可以根据以上内容将之前的爬虫文件xixixi.py进行相应的修改。如下所示:

代码语言:javascript复制
import scrapy
from First_Get.items import FirstGetItem

class XixixiSpider(scrapy.Spider):
    name = 'xixixi'
    allowed_domains = ['sina.com.cn']
    start_urls = ['http://slide.news.sina.com.cn/s/slide_1_2841_103185.html',
                  'http://slide.mil.news.sina.com.cn/k/slide_8_193_45192.html#p=1',
                  'http://news.sina.com.cn/p1/2016-09-12/doc-ifxvukhv8147404.shtml',
                  ]

    def parse(self, response):
        item = FirstGetItem()
        item["urlname"] = response.xpath("/html/head/title/text()")
        print(item["urlname"])

我们爬取了新浪的主域名(sina.com.cn)设置了爬取的起始网址。分别设置了3个网页、 我们用xpath进行了数据的提取,xpath("/html/head/title/text()"),就是将title标签中的文本提取了出来(xpath后面会详细的讲解) 我们用命令启动该文件:

Xpath基础:

之前我们在手写爬虫的时候,经常使用正则表达式来对爬取到的数据进行筛选和提取,而在Scrapy中,使用多的是Xpath表达式,用他来进行数据的筛选和提取。 Xpath是一种XML语言。

XML语言:可扩展标记语言,标准通用标记语言的子集,是一种用于标记电子文件使其具有结构性的标记语言。

在Xpath表达式中,使用"/"可以选择某个标签。并且可以进程多层查找。现在有下图所示代码:

如果要提取出

标签中对应的内容,

代码语言:javascript复制
可以使用	/html/body/h2 来实现筛选。

如果要提取出标签中的文本信息,可以通过text()来实现

代码语言:javascript复制
/html/body/h2/text()

如果要获取所有属性 X 的值为 Y 的 标签的内容,可以通过"//Z[@X="Y"]"的方法获取

比如获取所有属性为f1的<img>便签中的内容。可以这样: //img[@class=“f1”] 以上就是xpath表达式使用基础方面的内容了,很容易掌握的,有了这些基础,后面我们的提取信息就没太大问题了。

用XMLFeedSpider来分析XML源:

  • 如果想用Scrapy爬虫来处理XML文件,我们可以用XMLFeedSpider去实现。
  • 我们经常使用XMLFeedSpider去处理RSS订阅信息。RSS是一种信息聚合技术,可以让信息的发布更加高效便捷。而RSS是基于XML标准的。

用xmlfeed爬取新浪博客的订阅信息。 任意选择一个新浪博客,点击订阅出现一个订阅地址:http://blog.sina.com.cn/rss/1246151574.xml 文件是.xml结尾,这就是我们要爬取的xml标准的RSS订阅信息。 我们来创建1个项目:

代码语言:javascript复制
python -m scrapy startproject xmlpjt
cd xmlpjt
python -m scrapy genspider -t xmlfeed steve sina.com.cn

然后编写items.py。

代码语言:javascript复制
#coding:utf-8
import scrapy
class XmlpjtItem(scrapy.Item):
    title = scrapy.Field()
    link = scrapy.Field()
    author = scrapy.Field()

编写爬虫文件steve.py。

代码语言:javascript复制
#coding: utf-8
from scrapy.spiders import XMLFeedSpider
 
from xmlpjt.items import XmlpjtItem
 
 
class SteveSpider(XMLFeedSpider):
    name = 'steve'
    allowed_domains = ['sina.com.cn']
    start_urls = ['http://blog.sina.com.cn/rss/1246151574.xml']
    iterator = 'iternodes' # you can change this; see the docs
    itertag = 'rss' # change it accordingly
 
    def parse_node(self, response, selector):
        item = XmlpjtItem()
        item["title"] = selector.xpath("/rss/channel/item/title/text()").extract()
        item["link"] = selector.xpath("/rss/channel/item/link/text()").extract()
        item["author"] = selector.xpath("/rss/channel/item/author/text()").extract()
        for i in range(len(item["title"])):
            print("第" str(i 1) "篇文章")
            print("标题是:")
            print(item["title"][i])
            print("链接为:")
            print(item["link"][i])
            print("作者是:")
            print(item["author"][i])
            print("---------------------------------------")
        return item

运行项目:

代码语言:javascript复制
python -m scrapy crawl steve --nolog

可打印出:

XMLFeedSpider中常见的属性和方法及含义: (1)iterator属性:设置使用的迭代器,默认为“iternodes”(一个基于正则表达式的高性能迭代器),除此之外还有“html”和“xml”迭代器; (2)itertag:设置开始迭代的节点; (3)parse_node方法:在节点与所提供的标签名相符合时被调用,在其中定义信息提取和处理的操作; (4)namespaces属性:以列表形式存在,主要定义在文档中会被蜘蛛处理的可用命令空间; (5)adapt_response(response)方法:在spider分析响应前被调用; (6)process_results(response, results)方法:在spider返回结果时被调用,主要对结果在返回前进行最后的处理。

学会使用CSVFeedSpider:

使用爬虫不仅能处理XML文件的数据,还能够处理CSV文件的数据。 CSV文件是一种被用户广泛应用的相对简单、通用的文件格式,其储存的数据可以轻松的与表格的数据互相转换。

他在使用上跟上面的XMLFeedSpider很类似,区别在于它会一行一行的迭代,而不是一个节点一个节点的迭代。 每次迭代行的时候会调用parse_row()方法。

代码语言:javascript复制
#coding:utf-8
    from scrapy.spiders import CSVFeedSpider
    class MySpider(CSVFeedSpider):
        name = 'pangan.com'
        allowed_domains = ['pangan.com']
        start_urls = ['http://pangan.win/example.csv']
        delimiter = ','  # 默认,每个字段分隔符
        # quotechar = "'"  # 不理解
    
        headers = ['question', 'answer', 'author', 'agree']
    
        def parse_row(self, response, row):
            self.logger.info('Hi, this is a row!: %r', row)
            print row['question']  # row数据类型为dict

这份csv是这个模样…

Scrapy爬虫多开技能:

我们现在运行Scrapy项目中的爬虫文件,需要一个一个地运行,那么是否可以将对应的想运行的爬虫文件批量运行呢? 在Scrapy中,如果想批量运行爬虫文件,常见的两种方法: 1、使用CrawProcess实现 2、使用修改craw源码 自定义命令的方式实现

CrawProcess实现: 这种方法在官方文档里面有说明。

官方文档 在同一个进程中运行多个蜘蛛 默认情况下,Scrapy在您运行时为每个进程运行一个蜘蛛。但是,Scrapy支持使用内部API为每个进程运行多个蜘蛛。scrapy crawl 这是一个同时运行多个蜘蛛的示例:

代码语言:javascript复制
import scrapy
from scrapy.crawler import CrawlerProcess

class MySpider1(scrapy.Spider):
    # Your first spider definition
    ...

class MySpider2(scrapy.Spider):
    # Your second spider definition
    ...

process = CrawlerProcess()
process.crawl(MySpider1)
process.crawl(MySpider2)
process.start() 

使用相同的示例CrawlerRunner:

代码语言:javascript复制
import scrapy
from twisted.internet import reactor
from scrapy.crawler import CrawlerRunner
from scrapy.utils.log import configure_logging

class MySpider1(scrapy.Spider):
    # Your first spider definition
    ...

class MySpider2(scrapy.Spider):
    # Your second spider definition
    ...

configure_logging()
runner = CrawlerRunner()
runner.crawl(MySpider1)
runner.crawl(MySpider2)
d = runner.join()
d.addBoth(lambda _: reactor.stop())

reactor.run()

相同的示例,但通过链接 延迟顺序运行蜘蛛:

代码语言:javascript复制
from twisted.internet import reactor, defer
from scrapy.crawler import CrawlerRunner
from scrapy.utils.log import configure_logging

class MySpider1(scrapy.Spider):
    # Your first spider definition
    ...

class MySpider2(scrapy.Spider):
    # Your second spider definition
    ...

configure_logging()
runner = CrawlerRunner()

@defer.inlineCallbacks
def crawl():
    yield runner.crawl(MySpider1)
    yield runner.crawl(MySpider2)
    reactor.stop()

crawl()
reactor.run() 

修改craw源码: 1.在项目中的spiders同级目录下创建一个新文件,我这里命名为mycrawls,在该文件夹下创建一个Python文件,我这里为startspiders.py,在同级目录下再创建一个__init__.py文件

2.在Python的安装目录下找到 D:python36Libsite-packagesscrapycommandscrawl.py,

我们点击进去看看可以发现蜘蛛通过,里面的run()方法运行,其中spname是爬虫的名字,所以我们可以修改里面的代码使得3爬虫得以多开。

代码语言:javascript复制
import os
from scrapy.commands import ScrapyCommand
from scrapy.utils.conf import arglist_to_dict
from scrapy.utils.python import without_none_values
from scrapy.exceptions import UsageError
 
 
class Command(ScrapyCommand):
 
    requires_project = True
 
    def syntax(self):
        return "[options] "
 
    def short_desc(self):
        return "Run a spider"
 
    def add_options(self, parser):
        ScrapyCommand.add_options(self, parser)
        parser.add_option("-a", dest="spargs", action="append", default=[], metavar="NAME=VALUE",
                          help="set spider argument (may be repeated)")
        parser.add_option("-o", "--output", metavar="FILE",
                          help="dump scraped items into FILE (use - for stdout)")
        parser.add_option("-t", "--output-format", metavar="FORMAT",
                          help="format to use for dumping items with -o")
 
    def process_options(self, args, opts):
        ScrapyCommand.process_options(self, args, opts)
        try:
            opts.spargs = arglist_to_dict(opts.spargs)
        except ValueError:
            raise UsageError("Invalid -a value, use -a NAME=VALUE", print_help=False)
        if opts.output:
            if opts.output == '-':
                self.settings.set('FEED_URI', 'stdout:', priority='cmdline')
            else:
                self.settings.set('FEED_URI', opts.output, priority='cmdline')
            feed_exporters = without_none_values(
                self.settings.getwithbase('FEED_EXPORTERS'))
            valid_output_formats = feed_exporters.keys()
            if not opts.output_format:
                opts.output_format = os.path.splitext(opts.output)[1].replace(".", "")
            if opts.output_format not in valid_output_formats:
                raise UsageError("Unrecognized output format '%s', set one"
                                 " using the '-t' switch or as a file extension"
                                 " from the supported list %s" % (opts.output_format,
                                                                  tuple(valid_output_formats)))
            self.settings.set('FEED_FORMAT', opts.output_format, priority='cmdline')
 
    def run(self, args, opts):
        if len(args) < 1:
            raise UsageError()
        elif len(args) > 1:
            raise UsageError("running 'scrapy crawl' with more than one spider is no longer supported")
        spname = args[0]
 
        self.crawler_process.crawl(spname, **opts.spargs)
        self.crawler_process.start()

将crawl.py里的代码复制粘贴进之前创建的startspiders.py中,修改其中的run()方法。

代码语言:javascript复制
 def run(self, args, opts):
        # 获取爬虫列表
        spd_loader_list = self.crawler_process.spider_loader.list()
        # 遍历各爬虫
        for spname in spd_loader_list or args:
            self.crawler_process.crawl(spname, **opts.spargs)
            print('此时启动的爬虫为:%s'%spname)
 
        self.crawler_process.start()

然后在settings.py里面进行相应配置 代码为

代码语言:javascript复制
COMMANDS_MODULE = 'Mycrawl.mycrawls'

随后在命令行中进入该项目,输入scrapy -h 可以看到自定义爬虫命令已经出现了。 使用该自定义命令试试

避免被Ban:

ban 就是禁止的意思。之前打游戏一直有ban英雄的说法。 我们在运行爬虫的时候,如果爬取的网页较多,经常会遇到这种问题。可能会被ban掉,也就是对方的反爬虫机制。 之前在写爬虫项目的时候介绍过响应的反爬虫机制以及应对策略,那么在Scrapy爬虫项目中,主要通过以下方式来避免被禁止: 1、禁止Cookie 2、设置下载延时 3、使用IP池 4、使用用户代理池 5、其他方式。比如分布式爬取等。

1、禁止cookie: 大部分网站都是通过用户的cookie信息对用户进行识别和分析,此时我们可以通过禁用本地Cookie信息让对方网站无法识别出我们的会话信息,这样就不能禁止我们爬取了。 禁止cookie的方法,可以在settings.py文件中进行相应的设置

settings里面有这两句,这两行代码就是设置禁止使用Cookie的代码。我们想要Cookie,只需要把#COOKIES_ENANLED = False的注释去掉即可,修改为:

代码语言:javascript复制
#Disable cookies (enabled by default)
COOKIES_ENABLED = False

这样就可以让那些通过用户的Cookie信息对用户进行识别的网站无法识别我们,就不能通过设置Cookie禁止我们爬取。

2、设置下载延时: 这个很容易理解的,就是我们在爬取某些网站的时候,如果频率过快,则网站会判断你是在进行自动爬虫行为。识别后对我们进行相应的限制,比如禁止我们再爬取服务器上的网页等。

也是在settings里面进行设置即可。

上图是文件中的代码。我们看#DOWNLOAD_DELAY = 3这里,就是设置时间的具体代码。解除这一行的注释即可实现下载延时的配置,3代表3秒,想要几秒修改数值即可。可以为0.5,0.7等等

3、使用IP池: 有的网站会对用户的IP进行检测。如果同一个IP在短时间对自己服务器上的网页就行大量的爬取,那么也会被限制=。= 之前我们提到过代理服务器,利用不同的代理服务器可以获得不同的IP,所以我们可以组成一个大型的IP聚合池,也就是IP池。

4、使用用户代理池: 网站服务器可以识别爬取时候的用户代理User-Agent信息,通过他来判断我们使用的什么浏览器,什么的版本等信息。 我们之前在手写的时候也会模拟浏览器进行访问,现在我们可以模拟出一堆浏览器,然后让爬虫每次随机挑选一个浏览器进行模拟。

代码语言:javascript复制
[  #浏览器的身份象征
          #opera
            'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60',
            'Opera/8.0 (Windows NT 5.1; U; en)',
            'Mozilla/5.0 (Windows NT 5.1; U; en; rv:1.8.1) Gecko/20061208 Firefox/2.0.0 Opera 9.50',
            'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; en) Opera 9.50',
         #firefox
            'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0',
            'Mozilla/5.0 (X11; U; Linux x86_64; zh-CN; rv:1.9.2.10) Gecko/20100922 Ubuntu/10.10 (maverick) Firefox/3.6.10',
          #safari
            'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534.57.2 (KHTML, like Gecko) Version/5.1.7 Safari/534.57.2',
          #chrome
            'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36',
            'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11',
            'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.133 Safari/534.16',
         #360
            'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.11 TaoBrowser/2.0 Safari/536.11',
            'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.71 Safari/537.1 LBBROWSER',
         #taobao
            'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; LBBROWSER)',
         #猎豹
            'Mozilla/5.0 (Windows NT 5.1) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.84 Safari/535.11 SE 2.X MetaSr 1.0',
            'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; SV1; QQDownload 732; .NET4.0C; .NET4.0E; SE 2.X MetaSr 1.0)',
        # UC浏览器
            "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.122 UBrowser/4.0.3214.0 Safari/537.36",
    ]

上面是一些常用的UA池。我们可以模拟他们来伪装我们浏览器信息。

5、其他方法: 除了上面所说的方法外,还有一些其他的方法可以使我们不被ban掉,比如说使用分布式爬虫方式进行爬取。这里就不多说了。留待后面再讲解。

本章内容回顾:

本篇文章描述了 Items的编写、Spider的编写、 Xpath基础、XMLFeedSpider分析XML源、CSVFeedSpider、爬虫多开技能、避免被ban。内容还是很多的,辛苦了!

0 人点赞