在爬取一个网站时,想要爬去的数据同场分布在多个页面中,每个页面包含一部分数据以及通向其他页面的链接;往往想要获取到我们想要的数据,就必须提取链接进行访问,提取链接可使用SelectorLinkExtractor两种方法,我们就后一种方法进行简单的使用说明,至于为什么使用LinkExtractor,当然是因为其在提取大量链接时更加方便。(注:这里所说的爬取是指通过scrapy框架进行操作的,如果没有当然就需要安装了。)

下载通过一个简单的例子进行演示说明:(这里以起点免费作品页面进行演示)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ scrapy shell -s USER_AGENT="Mozilla/5.0" 'https://www.qidian.com/free/all'
...
2018-10-23 16:08:22 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://www.qidian.com/robots.txt> (referer: None)
2018-10-23 16:08:22 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://www.qidian.com/free/all> (referer: None)
[s] Available Scrapy objects:
[s]   scrapy     scrapy module (contains scrapy.Request, scrapy.Selector, etc)
[s]   crawler   <scrapy.crawler.Crawler object at 0x7f0458000390>
[s]   item       {}
[s]   request   <GET https://www.qidian.com/free/all>
[s]   response   <200 https://www.qidian.com/free/all>
[s]   settings   <scrapy.settings.Settings object at 0x7f045694c780>
[s]   spider     <DefaultSpider 'default' at 0x7f04564d3c88>
[s] Useful shortcuts:
[s]   fetch(url[, redirect=True]) Fetch URL and update local objects (by default, redirects are followed)
[s]   fetch(req)                 Fetch a scrapy.Request and update local objects
[s]   shelp()           Shell help (print this help)
[s]   view(response)   View response in a browser
>>>

我们以翻页为例,获取它的下一页链接,该页面是这样的:

代码获取一下该翻页链接:

1
2
3
4
5
>>> from scrapy.linkextractors import LinkExtractor
>>> le = LinkExtractor(restrict_css='div[class="lbf-pagination"]>ul>li:last-child')
>>> links = le.extract_links(response)
>>> links[0].url
'https://www.qidian.com/free/all?orderId=&vip=hidden&style=1&pageSize=20&siteid=1&pubflag=0&hiddenField=1&page=2'

获取到的url就是下一页的url,下面我们就可以完成Spider的提取下一页链接的任务。

简单创建一个爬虫项目:

1
2
3
4
5
6
$ scrapy startproject qidian
...
$ cd qidian
$ scrapy genspider qidianBook qidian.com
Created spider 'qidianBook' using template 'basic' in module:
qidian.spiders.qidianBook

下面我们开始编写spiders中的qidianBook.py,针对这个项目,我们仅仅获取一下所有小说的书名、作者、分类、简介:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# -*- coding: utf-8 -*-
import scrapy
from scrapy.linkextractors import LinkExtractor
from urllib.parse import urljoin


class QidianbookSpider(scrapy.Spider):
   name = 'qidianBook'
   allowed_domains = ['qidian.com']
   start_urls = [
       'https://www.qidian.com/free/all?orderId=&vip=hidden&style=1&pageSize=20&siteid=1&pubflag=0&hiddenField=1&page=1'
  ]

   def parse(self, response):
       baseUrl = response.url
       book_lis = response.xpath('//ul[@class="all-img-list cf"]/li')
       for li in book_lis:
           book_name = li.xpath('.//h4/a/text()').extract_first()
           book_author = li.xpath('.//p[@class="author"]/a[@class="name"]/text()').extract_first()
           book_type = '*'.join(li.css('p[class="author"]>a:nth-child(4)::text,a:nth-child(6)::text').extract())
           item = {
               '书名:': book_name,
               '作者:': book_author,
               '类型:': book_type,
          }
           url = li.css('h4>a::attr("href")').extract_first()
           complete_url = urljoin(baseUrl, url)
           yield scrapy.Request(complete_url, callback=self.get_intro, meta={'item': item})

       le = LinkExtractor(restrict_css='div[class="lbf-pagination"]>ul>li:last-child')
       links = le.extract_links(response)
       if links:
           next_url = links[0].url
           yield scrapy.Request(next_url, callback=self.parse)

   def get_intro(self, response):
       item = response.meta['item']
       intro = response.css('div[class="book-intro"]>p::text').extract_first().replace(' ', '').replace('\r', '').replace('\u3000', ' ')
       item['简介:'] = intro
       return item

然后我们执行这个爬虫,执行之前可以在settings中添加一个USER_AGENT

1
USER_AGENT = 'Mozilla/5.0 (X11; Ubuntu; Linu…) Gecko/20100101 Firefox/62.0'

执行,并把结果保存到qidian.json中:

1
$ scrapy crawl qidianBook -o qidian.json -s FEED_EXPORT_ENCODING=utf-8

你可得有心理准备,这一运行,就会把所有书爬下来。
之后你就可以查看qidian.json文件里的内容了:

1
2
3
4
5
6
7
8
9
10
$ cat qidian.json
[
{"书名:": "神级生灵", "作者:": "魔动", "类型:": "玄幻*东方玄幻", "简介:": " 穿越不是神话,这是龙炅的座右铭,也只是座右铭!"},
{"书名:": "云穹之未来断点", "作者:": "骷髅精灵", "类型:": "科幻*未来世界", "简介:": " 异族入侵太阳系,血狼战队队长莫峰在一场惨烈的会战之后回到了末日之前,他能否解开谜团,改变身边亲人朋友的命运……"},
{"书名:": "最遥远的南边", "作者:": "哦罗罗", "类型:": "现实*成功励志", "简介:": " 几十个英文单词储备就远赴海外,是头脑发热还是......?"},
{"书名:": "保持匿名", "作者:": "莫远遥", "类型:": "现实*现实百态", "简介:": " 突然发现至少20个字的介绍不知该说些什么,其实,连作品类型,我也不确切我选对没有,它当然和“玄幻”“奇幻”“武侠”“仙侠”“军事”“历史”“游戏”“体育”“灵异”“女生”“二次元”这类无关,天,完全不入流的啊,还有谁会看,“科幻”的话,应该也不算,那就只剩下都市和职场了,可是,那些奇人神人离奇幻想狗血剧情,我实在不会啊,也罢也罢,怎么说,小说背景也算是在都市吧,那作品类型就选“都市”吧,可是子类“异术超能”“恩怨情仇”“青春校园”……果然,大家都爱看些充满幻想的东西,算了算了,随意选吧,就当,这里是我一个人的菜园子,每天来浇点水松松土种种菜,也就不求有人问津了。"},
{"书名:": "开天录", "作者:": "血红", "类型:": "玄幻*东方玄幻", "简介:": " 生存,很容易。"},
{"书名:": "红砖街轶事", "作者:": "李安云", "类型:": "现实*现实百态", "简介:": " 一条闹中取静、建筑独特的老街"},
......
]

总结:

LinkExtractor的使用更方便的获取打了下一页的链接,代码简洁,这仅仅是其一种方式的使用,更多参数请参考Link Extractors