2021-06-18

XPath

全称XML Path Language ,即XML路径语言,它是一门在XML文档中查找信息的语言,最初是用来搜寻XML文档的,但是它同样适用于HTML文档的搜索。我们在做爬虫时,完全可以用XPath来做相应的信息抽取。

Xpath的选择功能非常强大,它提供了非常简明的路径选择表达式,另外,它还提供了超过00个内建函数,用于字符串、数值、时间的匹配以及节点、序列的处理等。几乎所有我们想要定位的节点,都可以用XPath来选择。

XPath常用的规则

nodename:选取此节点所有子节点

/:从当前节点选取直接子节点

//:从当前节点选取子孙节点

.选取当前节点

..选取当前节点的父节点

@ 选取属性

 

from lxml import  etree
text='''
<div>
<ul>
<li class='item-0'> <a href='link1.html'>first item</a><li>
<li class='item-1'> <a href='link2.html'>second item</a><li>
<li class='item-inactive'> <a href='link3.html'>third item</a><li>
<li class='item-1'> <a href='link4.html'>fourth item</a><li>
<li class='item-0'> <a href='link5.html'>fifth item</a>
</ul>
</div>
'''
html=etree.HTML(text)
result=etree.tostring(html)
print(result.decode('utf-8'))

这里首先导入lxml库的etree模块,然后声明了一段HTML文本,调用HTML类进行初始化,这样就成功构造了一个XPath解析对象。这里需要注意的是,HTML文本中的最后一个li节点是没有闭合的,但是etree模块可以自动修正HTML文本。

这里我们调用tostring() 方法即可输出修正后的HTML代码,但是结果是bytes类型。这里用decode() 方法将其转成str类型

 

可以看到,经过处理之后,li节点标签被补全,并且还自动添加了body,html节点。

另外也可以直接读取文本文件进行解析

test.html里面的内容

<div>
<ul>
<li class='item-0'> <a href='link1.html'>first item</a><li>
<li class='item-1'> <a href='link2.html'>second item</a><li>
<li class='item-inactive'> <a href='link3.html'>third item</a><li>
<li class='item-1'> <a href='link4.html'>fourth item</a><li>
<li class='item-0'> <a href='link5.html'>fifth item</a>
</ul>
</div>
from lxml import etree
html=etree.parse('./test.html',etree.HTMLParser())
result=etree.tostring(html)
print(result.decode('utf-8'))

所有节点

我们一般会用//开头的XPath规则来选取所有符合要求的节点。这里以上面的HTML文档为例,如果要选取所有节点,可以这样实现:

from lxml import etree
html=etree.parse('./test.html',etree.HTMLParser())
result=html.xpath('//*')
print(result)

这里使用*代表匹配所有节点,也就是整个HTML文档中所有节点都会被获取。可以看到,返回形式是一个列表,每个元素是Element类型,后跟了节点的名称。

当然,此处匹配也可以指定节点名称,如果想获取所有li节点,示例如下:

from lxml import etree
html=etree.parse('./test.html',etree.HTMLParser())
result=html.xpath('//li')
print(result)
print(result[0])

这里可以看到提取结果是一个列表形式,其中每个元素都是一个Element对象。如果要取出其中一个对象,可以直接用中括号加索引,如[0]

子节点

我们通过/或//即可查找元素的子节点或子孙节点。假如现在想选择li节点的所有直接a子节点,可以这样实现

 

这里通过追加/a即选择了所有li节点的所有直接a子节点。因为//li用于选中所有li节点,/a用于选中li节点的所有子节点a,二者组合在一起即获取所有li节点的所有直接a子节点。

from lxml import etree
html=etree.parse('./test.html',etree.HTMLParser())
result=html.xpath('//li/a')
print(result)
print(result[0])

此处的/用于选取直接子节点,如果要获取所有子孙节点,就可以使用//。例如,要获取ul节点下的所有子孙a节点,就可以这样实现:

from lxml import etree
html=etree.parse('./test.html',etree.HTMLParser())
result=html.xpath('//ul//a')
print(result)
print(result[0])

但是如果用//ul/a。就无法获取任何结果了。这里/用于获取直接子节点,而在ul节点下没有直接的a子节点,只有里节点,所以就无法获取任何匹配结果,代码如下:

from lxml import etree
html=etree.parse('./test.html',etree.HTMLParser())
result=html.xpath('//ul/a')
print(result)
print(result[0])

因此。这里要注意/和//的区别,其中/用于获取直接子节点,//用于获取子孙节点

父节点

查找父节点用..来实现

比如,现在首先选中href属性为link4.html的a节点,然后再获取其父节点,然后再获取其class属性,相关代码如下:

from lxml import etree
html=etree.parse('./test.html',etree.HTMLParser())
result=html.xpath('//a[@href="link4.html"]/../@class')
print(result)
print(result[0])

也可以通过parent::来获取父节点,代码如下:

from lxml import etree
html=etree.parse('./test.html',etree.HTMLParser())
result=html.xpath('//a[@href="link4.html"]/parent::*/@class')
print(result)

属性匹配

在选取的时候,我们还可以用@符号进行属性过滤。比如,这里如果要选取class为item-0的li节点,可以这样实现:

from lxml import etree
html=etree.parse('./test.html',etree.HTMLParser())
result=html.xpath('//li[@class="item-0"]')
print(result)

这里我们通过[@class="item-0"],限制了节点的 class属性为item-0,而HTML文本符合条件的li节点有两个,所以结果应该返回两个匹配到的元素。

是不是正确的那两个?

文本获取

首先使用XPath中的text()方法获取节点中的文本,接下来尝试获取前面li节点中的文本,相关代码如下:

from lxml import etree
html=etree.parse('./test.html',etree.HTMLParser())
result=html.xpath('//li[@class="item-0"]/text()')
print(result)

获取到换行符,因为XPath中的text()前面是/,而此处/的含义是选取直接子节点,很明显li的直接子节点都是a节点,文本都是在a节点内部的,所以这里匹配到的结果就是被修正的li节点内部的换行符,因为自动修正的li节点的尾部标签换行了。

 

 

其中一个节点因为自动修正,li节点的尾标签添加的时候换行了,所以提取文本得到的唯一结果就是li节点的尾标签和a节点的尾标签之间的换行符。

因此,如果想要获取li节点内部的文本,就有两种方式,一种是先选取a节点再获取文本,另一种就是使用//。接下来,我们来看下二者的区别。

html=etree.parse('./test.html',etree.HTMLParser())
result=html.xpath('//li[@class="item-0"]/a/text()')
print(result)

内容都是属性为item-0的li节点的文本,这也印证了前面属性匹配的结果是正确的

 

这里我们是逐层选取的,现选取了li节点,又利用/选取了其直接子节点a,然后再选取其文本,得到的结果恰好是符合我们预期的两个结果。

from lxml import etree
html=etree.parse('./test.html',etree.HTMLParser())
result=html.xpath('//li[@class="item-0"]//text()')
print(result)

这里返回的结果是三个,这里是选取所有子算节点的文本,其中前两个就是li的子节点a节点内部的文本,另外一个就是最后一个里节点内部的文本,即换行符

所以说,如果想要获取子孙节点内部的所有文本,可以直接用//加text()的方式,这样可以保证获取最全面的文本信息,但是可能会夹杂一些换行符等特殊字符。如果想要获取某些特定子孙节点下的所有文本,可以先选取到特定的子孙节点,然后再调用text()方法获取其内部文本,这样就可以保证获取的结果是整洁的。

属性获取

我们知道用text()可以获取节点内部文本,那么节点属性应该怎样获取?,其实还是用@符号就可以了,例如,我们想获取所有li节点下所有a节点的href属性,代码如下:

from lxml import etree
html=etree.parse('./test.html',etree.HTMLParser())
result=html.xpath('//li/a/@href')
print(result)

可以看到,我们成功获取了所有li节点下a节点的href属性,它们以列表形式返回。

Beautiful Soup

强大的解析工具Beautiful Soup,它借助网页的结构和属性等特性来解析网页,有了它,不用再去写一些复杂的正则表达式,只需要简单的几条语句,就可以网页某个元素的提取。

Beautiful Soup 在解析时实际上依赖,它除了支持python标准库中的html解析器外,还支持一些第三方解析器 用lxml解析器,使用lxml,在初始化Beautiful Soup 时,可以把第二个参数改为lxml即可

 

在后面统一使用lxml解析器来进行演示

import requests
from bs4 import BeautifulSoup
html='''
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title" name="dromouse"><b>The Dormouse's story</b></p>
<p class="story"> Once upon a time there were three little sisters;and their names were
<a href="http"//example.com/elsie" class="sister" id="link1"><!--Elsie--></a>,
<a href="http"//example.com/lacie" class="sister" id="link2"><!--Lacie--></a> and
<a href="http"//example.com/tillie" class="sister" id="link3"> Tillie </a>;
and they lived at the bottom of a well.</p>
<p class="story">...</p>
'''
soup=BeautifulSoup(html,'lxml')
print(soup.prettify())
print(soup.title.string)

这里首先声明变量html,它是一个HTML字符串,但是需要注意的时,它不是一个完整的HTML字符串,因为body和html节点都没有闭合。接着,我们将它当做第一个参数传给BeatutifulSoup对象,该对象的第二个参数为解析器的类型(这里使用lxml),此时就完成了BeautifulSoup对象的初始化,然后,将这个对象赋值给soup变量。

解析来,就可以调用soup的各个方法和属性解析这串HTML代码

首先,调用prettify()方法,这个方法可以把要解析的字符串以标准的缩进格式输出,这里需要注意的是,输出结果里面包含body和html节点,也就是说对于不标准的HTML字符串BeautifulSoup可以自动更正格式。这一步不是由prettify()方法做的,而是在初始化BeautifulSoup时就完成了

然后调用soup.title.string,这实际输出的时HTML中title节点的文本内容。所以,soup.title可以选出HTML中的title节点,再调用string属性就可以得到里面的文本了,所以我们可以通过点单调用几个属性就可以完成文本的提取,是不是非常方便。

节点选择器

直接调用节点的名称就可以选择节点元素,再调用string属性就可以得到节点内的文本了,这种选择方式速度非常快,如果单个节点结构层次非常清晰,可以选用这种方式来解析、

import requests
from bs4 import BeautifulSoup
html='''
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title" name="dromouse"><b>The Dormouse's story</b></p>
<p class="story"> Once upon a time there were three little sisters;and their names were
<a href="http"//example.com/elsie" class="sister" id="link1"><!--Elsie--></a>,
<a href="http"//example.com/lacie" class="sister" id="link2"><!--Lacie--></a> and
<a href="http"//example.com/tillie" class="sister" id="link3"> Tillie </a>;
and they lived at the bottom of a well.</p>
<p class="story">...</p>
'''
soup=BeautifulSoup(html,'lxml')
print(soup.title)
print(type(soup.title))
print(soup.title.string)
print(soup.head)
print(soup.p)

这里依然选用刚才的HTML代码,首先打印输出title节点的选择结果,输出结果时title节点加里面的文字内容。接下来,输出它的类型,时bs4.element.Tag类型,这是BeautifulSoup中一个重要的数据结构。经过选择器选择后,选择结果都是这种Tag类型,Tag具有一些属性,比如string属性,调用该属性,可以得到节点的文本内容,所以接下来的输出结果正是节点的文本内容。

接下来,我们又尝试选择head节点,结果又是节点加其内容的所有内容。最后,选择了p节点。不过结果是第一个p节点的内容,后面的几个p节点并没有找到。也就是说,当有多个节点时,这种选择方式只会选择到第一个匹配的节点,其他的后面节点都会忽略。

提取信息

string属性获取文本的值,那么如何获取节点属性的值?

1 获取名称

可以利用name属性获取节点的名称,这里还是以上面文本为例子,选取title节点,然后调用name属性就可以得到节点名称

soup=BeautifulSoup(html,'lxml')
print(soup.title.name)

2 获取属性

每个节点可能有多个属性,比如id和class等,选择这个节点元素后,可以调用attrs获取所有属性:

soup=BeautifulSoup(html,'lxml')
print(soup.p.attrs)
print(soup.p.attrs['name'])

可以看到,attrs的返回结果是字典形式,它把选择的节点的所有属性和属性值组合成一个字典。接下来,如果要获取name属性,就相当于从字典中获取某个键值,只需要用中括号加属性名就可以了。比如,要获取name属性,就可以通过attrs['name']来得到。

其实这样有点烦琐,还有一种更简单的获取方式:可以不用写attrs,直接在节点元素后面加中括号,传入属性名就可以获取属性值

soup=BeautifulSoup(html,'lxml')
print(soup.p['name'])
print(soup.p['class'])

这里需要注意的是,有的返回的是字符串,有的返回结果是字符串组成的列表,比如name属性返回的值是唯一的,返回的结果就是单个字符串,而对于class,一个节点元素可能有多个class,所以返回的是列表

获取内容

可以利用string属性获取节点元素包含的文本内容,比如要获取第一个p节点的文本:

soup=BeautifulSoup(html,'lxml')
print(soup.p.string)

p节点是第一个p节点,获取的文本也是第一个p节点里面的文本

嵌套选择

在上面的例子中,我们知道每一个返回结果都是bs4.element.Tag类型,它同样可以继续调用节点进行下一步的选择,比如,我们获取了head节点元素,我们可以继续调用head来选取其内部的head节点元素

import requests
from bs4 import BeautifulSoup
html='''
<html><head><title>The Dormouse's story</title></head>
<body>
'''
soup=BeautifulSoup(html,'lxml')
print(soup.head.title)
print(type(soup.head.title))
print(soup.head.title.string)

第一行结果是调用head之后再次调用title而选择title节点元素。然后打印输出了它的类型,可以看到,它仍然是bst.element.Tag类型。也就是说,我们在Tag类型基础上再次选择得到的依然是Tag类型,最后,输出它的string属性,也就是节点里的文本内容。

方法选择器

前面所讲选择方法都是通过属性来选择的,这种方法非常快,但是如果进行比较复杂的选择,就比较麻烦,因此bs还提供了一些查询方法,调用它们,然后传入相应参数就可以了

find_all(name,attrs,recursive,text,**kwargs)

1name

可以根据节点名来查询元素

html="""
<div class="panel">
<div class="panel-heading">
<h4> hello <h4>
</div>
<div class="panel-body">
<ul class="list" id="list-1">
<li class="element">Foo</li>
<li class="element">Bar</li>
<li class="element">jAY</li>
</ul>
<ul class="list list-small" id="list-2">
<li class="element"> Foo</li>
<li class="element">Bar</li>
</ul>
</div>
</div>
"""
from bs4 import  BeautifulSoup
soup=BeautifulSoup(html,'lxml')
print(soup.find_all(name='ul'))
print(type(soup.find_all(name='ul')[0]))

调用find_all()方法,传入name参数,参数值为ul也就是说我们查询所有ul节点,返回是列表类型,长度为2,每个元素依然都是bs4.element.Tag

2.attrs

根据节点名查询,也可以传入一些属性来查询

from bs4 import  BeautifulSoup
soup=BeautifulSoup(html,'lxml')
print(soup.find_all(attrs={'id':'list-1'}))
print(soup.find_all(attrs={'name':'elements'}))

这里查询的时候传入的是attrs参数,参数的类型是字典类型。比如,要查询id为list-1的节点,可以传入attrs={'id':'list-1'}的查询条件,得到的结果是列表形式,包含的内容就是符合id为list-1的所有节点

对于一些常用的属性,比如id和class等,我们可以不用attrs来传递,比如要查询id为list-1的节点,可以直接传入id这个参数。还是上面的文本,我们换一种方式来查询。

from bs4 import  BeautifulSoup
soup=BeautifulSoup(html,'lxml')
print(soup.find_all(id='list-1'))
print(soup.find_all(class_='element'))

对于class来说,由于class在python里是一个关键字,所以需要后面加一个下划线

3text

text参数可以用来匹配节点的文本,传入的形式可以是字符串,可以是正则表达式对象,

import re
​
html="""
<div class="panel">
<div class="panel-body">
<a>hello,this is a link</a>
<a> hello,this is a link,too</a>
</div>
</div>
"""
from bs4 import  BeautifulSoup
soup=BeautifulSoup(html,'lxml')
print(soup.find_all(text=re.compile('link')))

这里有两个a节点,其内部包含文本信息,这里在find_all()方法中传入text参数,该参数为正则表达式对象,结果返回所有匹配正则表达式的节点文本组成的列表。

find()方法

find返回的是单个元素,也就是第一个匹配的元素,而前者返回的是所有匹配的元素组成的列表

html="""
<div class="panel">
<div class="panel-heading">
<h4> hello <h4>
</div>
<div class="panel-body">
<ul class="list" id="list-1">
<li class="element">Foo</li>
<li class="element">Bar</li>
<li class="element">jAY</li>
</ul>
<ul class="list list-small" id="list-2">
<li class="element"> Foo</li>
<li class="element">Bar</li>
</ul>
</div>
</div>
"""
from bs4 import  BeautifulSoup
soup=BeautifulSoup(html,'lxml')
print(soup.find(name='ul'))
print(type(soup.find(name='ul')))
print(soup.find(class_='list'))

这里返回的结果不再是列表形式,而是第一个匹配的节点元素,类型依然是Tag类型

Scrapy

1、创建一个Scrapy项目

2、创建一个spider来抓取站点和处理数据

3、通过命令行将抓取的内容到处

4、将抓取内容保存到MongoDB数据库

1、需要安装scrapy框架,MongoDB和PyMongo库,

3.创建项目

创建一个scrapy项目,项目文件可以直接用scrapy命令生成,命令如下所示

scrapy startproject tutorial

这个命令可以在任意文件夹运行。如果提示权限问题,可以加sudo运行该命令。这个命令会创建一个名为tutorial的文件夹

文件夹结构如下

scrapy.py #scrapy 部署时的配置文件

tutorial#项目的某块,需要在这里引入

init.py

items.py Items的定义,定义爬虫的数据结构

middlewares.py Middlewares 的定义,定义爬取时的中间件

pipelines.py#Pipelines的定义,定义数据管道

settings.py#配置文件

spiders #放置Spiders的文件夹

init.py

4.创建Spider

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

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

cd tutorial

scrapy genspider quotes quotes.toscrape.com

进入刚才创建的tutorial文件夹,然后执行genspider命令。第一个参数是Spider的名称,第二个参数是网站域名,执行完毕后,spiders文件夹多了一个quotes.py 它就是刚刚创建的Spider,内容如下:

import scrapy
​
​
class QuotesSpider(scrapy.Spider):
    name = 'quotes'
    allowed_domains = ['quotes.toscrape.com']
    start_urls = ['http://quotes.toscrape.com/']
​
    def parse(self, response):
        pass

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

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

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

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

5 创建Item

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

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

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

class QuoteItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    text=scrapy.Field()
    author=scrapy.Field()
    tags=scrapy.Field

这里定义了三个字段,将类的名称修改为QuoteItem,接下来爬取时我们会使用到这个Item

6 解析Response

前面我们看到,parse()方法的参数response是start_urls 里面的链接爬取后的结果。所以在parse()方法中,我们可以直接对response变量包含的内容进行解析,比如浏览器请求结果的网页源代码,或者进一步分析源代码内容,或者找出结果中的url链接而得到下一个请求。

网页结构,每一页都有多个class为quote的区块,每个区块内都包含text,author,tags。那么我们找出所有的quote,然后提取每一个quote中的内容。

提取的放肆可以是css选择器或者XPath选择器,在这里我们使用CSS选择器进行选择 parse()改写如下

def parse(self, response):
    quotes=response.css('.quote')
    for quote in quotes:
        text=quote.css('.text::text').extract_first()
        author=quote.css('.author::text').extract_first()
        tags=quote.css('.tags.tag::text').extract()

这里首先利用选择器选择所有的quote,并将其赋值为quotes变量,然后利用for循环对每个quote遍历,解析每个quote的内容

对text来说,观察到它的class为text,所以可以用.text选择器来选取,这个结果实际上是整个带有标签的节点,要获取它的正文内容,可以加::text来获取。结果是长度为一的列表,所以还需要用etract_first()方法来获取第一个元素。而对于tags来说,由于我们要获取所有的标签,所以用extract()方法获取整个列表即可。

7使用ITem

上文定义了Item,接下来就要使用它了,Item可以理解为一个字典,不过在声明的时候需要实例化,然后依次用刚才解析的结果赋值Item的每一个字段,最后将Item返回即可。QuotesSpider的改写如下所示:

import scrapy
from tutorial.items import QuoteItem
class QuotesSpider(scrapy.Spider):
    name = 'quotes'
    allowed_domains = ['quotes.toscrape.com']
    start_urls = ['http://quotes.toscrape.com/']
    def parse(self, response):
        quotes=response.css('.quote')
        for quote in quotes:
            item=QuoteItem()
            item['text']=quote.css('.text::text').extract_first()
            item['author']=quote.css('.author::text').extract_first()
            item['tags'] = quote.css('.tags.tag::text').extract()
            yield item

如此一来,首页的所有内容被解析出来,并被赋值给一个个QuoteItem

8后续Request

上面的操作实现了从初始页面抓取内容。那么,下一页的内容该如何抓取?这就需要我们从当前页面中找到信息来生成下一个请求,然后在下一个请求的页面里找到信息再构造一个请求。这样循环往复迭代,从而实现整站的爬取。

将刚才的页面拉到最底部。

这里有一个next按钮。查看源代码,发现其链接,通过这个链接我们就可以构造下一个请求。

构造请求时需要用到scrapy.Request。这里我们传递两个参数----url和callback,这两个参数的说明一下。

url:它是请求链接

callback:它是回调函数,当指定了该回调函数的请求完成之后,获取到响应,引擎会将该响应作为参数传递给这个回调函数。回调函数进行解析或生成下一个请求,回调函数如上文的parse()所示。

由于parse()就是解析text、author、tags的方法,而下一页的结构和刚才已经解析的页面结构是一样的,所以我们可以再次使用parse()方法来做页面解析

接下来我们要做的就是利用选择器得到下一页链接并生成请求,在parse()方法后追加如下的代码:

next=response.css('.pager .next a::attr("href")').extract_first()
url=response.urljoin(next)
yield scrapy.Request(url=url,callback=self.parse)

第一句代码首先通过CSS选择器获取下一个页面的链接,即要获取a超链接中的href属性。这里用到了::attr(href)操作。然后再调用extract_first()方法获取内容。

第二代码调用了urljoin()方法,urljoin()方法可以将相对URL构造一个绝对的URL。例如获取到的下一页地址是/page/2 ,urljoin()方法处理后得到的结果就是完整的域名

第三局代码通过url和callback变量构造了一个新的请求,回调函数callback依然使用parse()方法。这个请求完成后,响应会重新经过parse()方法处理,得到第二页的解析结构,然后生成第二页的下一页,也就是第三页的请求。这样爬虫就进入了一个循环,直到最后一页。

import scrapy
from tutorial.items import QuoteItem
class QuotesSpider(scrapy.Spider):
    name = 'quotes'
    allowed_domains = ['quotes.toscrape.com']
    start_urls = ['http://quotes.toscrape.com/']
    def parse(self, response):
        quotes=response.css('.quote')
        for quote in quotes:
            item=QuoteItem()
            item['text']=quote.css('.text::text').extract_first()
            item['author']=quote.css('.author::text').extract_first()
            item['tags'] = quote.css('.tags.tag::text').extract()
            yield item
        next=response.css('.pager .next a::attr("href")').extract_first()
        url=response.urljoin(next)
        yield scrapy.Request(url=url,callback=self.parse)

9运行

接下来,进入目录,运行如下命令

scrapy crawl quotes

就可以看到Scrapy的运行结果了

 

首先输出Scrapy当前的版本号以及正在启动的项目名称。接着输出了当前settings.py中一些重写后的配置。然后输出了当前所用的Middlewares和Pipelines。Middlewares默认是启用的可以在settings.py中修改。Pipelines默认是空,同样也可以在settings.py中配置。

接下来就是输出各个页面的抓取结果,可以看到爬虫一边解析,一边翻页,直到将所有内容抓取完毕。

最后Scrapy输出了整个抓取过程的统计信息,如请求的字节数、请求次数、响应次数、完成原因等。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值