文章目录
- 使用 LinkExtractor 提取链接
- 1. 提取链接的方法
- (1)使用Selector
- (2)使用LinkExtractor
- 2. LinkExtractor 提取链接的规则
- (1)allow
- (2)deny
- (3)allow_domains
- (4)deny_domains
- (5)restrict_xpaths
- (6)restrict_css
- (7)tags
- (8)attrs
- (9)process_value
使用 LinkExtractor 提取链接
1. 提取链接的方法
在爬取一个网站时,想要爬取的数据通常分布在多个页面中,每个页面包含一部分数据以及到其他页面的链接,提取页面中数据的方法大家已经掌握,提取链接有使用 Selector 和使用 LinkExtractor 两种方法。
(1)使用Selector
因为链接也是页面中的数据,所以可以使用与提取数据相同的方法进行提取,在提取少量链接或提取规则比较简单时,使用 Selector 就足够了。例如我们在项目 toscrapy
中编写爬虫 books.py 的数据解析函数 parse()
时,就是用了 Selector 提取了下一个页面的链接,代码如下:
python">class BooksSpider(scrapy.Spider):
name = 'books'
allowed_domains = ['books.toscrape.com']
start_urls = ['http://books.toscrape.com/']
def parse(self, response):
......
# 提取链接
next_url = response.css('ul.pager li.next a::attr(href)').extract_first()
if next_url:
next_url = response.urljoin(next_url)
yield scrapy.Request(next_url, callback=self.parse)
第一种方法我们早已掌握,下面学习如何使用 LinkExtractor 提取链接。
(2)使用LinkExtractor
Scrapy 提供了一个专门用于提取链接的类 LinkExtractor,在提取大量链接或提取规则比较复杂时,使用 LinkExtractor 更加方便。
LinkExtractor 的使用非常简单,我们通过将上述代码中的 Selector 替换成 LinkExtractor 进行讲解,代码如下:
python">from scrapy.linkextractors import LinkExtractor
class BooksSpider(scrapy.Spider):
name = 'books'
allowed_domains = ['books.toscrape.com']
start_urls = ['http://books.toscrape.com/']
def parse(self, response):
...
# 提取链接
le = LinkExtractor(restrict_css='ul.pager li.next')
links = le.extract_links(response)
if links:
next_url = links[0].url
yield scrapy.Request(next_url, callback=self.parse)
代码解析如下:
- Step 1:创建一个 LinkExtractor 对象,使用一个或多个构造器参数描述提取规则。这里传递给
restrict_css
参数一个 CSS 选择器表达式。它描述出下一页链接所在的区域(在 li.next 下)。 - Step 2:调用 LinkExtractor 对象的
extract_links
方法传入一个 Response 对象,该方法依据创建对象时所描述的提取规则, 在 Response 对象所包含的页面中提取链接,最终返回一个列表,其中的每一个元素都是一个 Link 对象,即提取到的一个链接。 - Step 3:由于页面中的下一页链接只有一个,因此用 links[0] 获取 LinkExtractor 对象,LinkExtractor 对象的 url 属性便是链接页面的绝对 url 地址(无须再调用
response.urljoin
方法),用其构造 Request 对象并提交。
2. LinkExtractor 提取链接的规则
接下来,我们来学习使用 LinkExtractor 的构造器参数描述提取链接的规则。
首先我们创建两个包含多个链接的HTML页面,作为 LinkExtractor 提取链接的示例网页:
<!-- example1.html -->
<html>
<body>
<div id="top">
<p>下面是一些站内链接</p>
<a class="internal" href="/intro/install.html">Installation guide</a>
<a class="internal" href="/intro/tutorial.html">Tutorial</a>
<a class="internal" href="../examples.html">Examples</a>
</div>
<div id="bottom">
<p>下面是一些站外链接</p>
<a href="http://stackoverflow.com/tags/scrapy/info">StackOverflow</a>
<a href="https://github.com/scrapy/scrapy">Fork on Github</a>
</div>
</body>
</html>
<!-- example2.html -->
<html>
<head>
<script type='text/javascript' src='/js/app1.js'/>
<script type='text/javascript' src='/js/app2.js'/>
</head>
<body>
<a href="/home.html">主页</a>
<a href="javascript:goToPage('/doc.html'); return false">文档</a>
<a href="javascript:goToPage('/example.html'); return false">案例</a>
</body>
</html>
LinkExtractor 构造器的所有参数都有默认值,如果构造对象时不传递任何参数(使用默认值),就提取页面中所有链接。例如以下代码将提取页面 example1.html 中的所有链接:
python">from scrapy.http import HtmlResponse
from scrapy.linkextractors import LinkExtractor
html1 = open('LE_Example1.html').read()
response1 = HtmlResponse(url='http://example1.com', body=html1, encoding='utf8')
le = LinkExtractor()
links = le.extract_links(response1)
print([link.url for link in links])
# 运行结果:
['http://example1.com/intro/install.html', 'http://example1.com/intro/tutorial.html', 'http://example1.com/examples.html', 'http://stackoverflow.com/tags/scrapy/info', 'https://github.com/scrapy/scrapy']
下面依次介绍 LinkExtractor 构造器的各个参数:
(1)allow
allow 接收一个正则表达式或一个正则表达式列表,提取绝对 url 与正则表达式匹配的链接,如果该参数为空(默认),就提取全部链接。
例1:提取页面 example1.html
中路径以 /intro
开始的链接:
python">from scrapy.http import HtmlResponse
from scrapy.linkextractors import LinkExtractor
html1 = open('LE_Example1.html').read()
response1 = HtmlResponse(url='http://example1.com', body=html1, encoding='utf8')
pattern = '/intro/.+\.html$'
le = LinkExtractor(allow=pattern)
links = le.extract_links(response1)
print([link.url for link in links])
# 运行结果:
['http://example1.com/intro/install.html', 'http://example1.com/intro/tutorial.html']
(2)deny
接收一个正则表达式或一个正则表达式列表,与 allow 相反,排除绝对 url 与正则表达式匹配的链接。
例2:提取页面 example1.html
中所有站外链接(即排除站内链接):
python">from scrapy.http import HtmlResponse
from scrapy.linkextractors import LinkExtractor
from urllib.parse import urlparse
html1 = open('LE_Example1.html').read()
response1 = HtmlResponse(url='http://example1.com', body=html1, encoding='utf8')
pattern = '^' + urlparse(response1.url).geturl()
le = LinkExtractor(deny=pattern)
links = le.extract_links(response1)
print([link.url for link in links])
# 运行结果:
['http://stackoverflow.com/tags/scrapy/info', 'https://github.com/scrapy/scrapy']
(3)allow_domains
接收一个域名或一个域名列表,提取到指定域的链接。
例3::提取页面 example1.html
中所有到 github.com
和 stackoverflow.com
这两个域的链接:
python">from scrapy.http import HtmlResponse
from scrapy.linkextractors import LinkExtractor
html1 = open('LE_Example1.html').read()
response1 = HtmlResponse(url='http://example1.com', body=html1, encoding='utf8')
domains = ['github.com', 'stackoverflow.com']
le = LinkExtractor(allow_domains=domains)
links = le.extract_links(response1)
print([link.url for link in links])
# 运行结果:
['http://stackoverflow.com/tags/scrapy/info', 'https://github.com/scrapy/scrapy']
(4)deny_domains
接收一个域名或一个域名列表,与 allow_domains 相反,排除到指定域的链接。
例4:提取页面 example1.html
中除了到 github.com
域以外的链接:
python">from scrapy.http import HtmlResponse
from scrapy.linkextractors import LinkExtractor
html1 = open('LE_Example1.html').read()
response1 = HtmlResponse(url='http://example1.com', body=html1, encoding='utf8')
le = LinkExtractor(deny_domains='github.com')
links = le.extract_links(response1)
print([link.url for link in links])
# 运行结果:
['http://example1.com/intro/install.html', 'http://example1.com/intro/tutorial.html', 'http://example1.com/examples.html', 'http://stackoverflow.com/tags/scrapy/info']
(5)restrict_xpaths
接收一个 XPath 表达式或一个 XPath 表达式列表,提取 XPath 表达式选中区域下的链接。
例5:提取页面 example1.html
中 <div id="top">
元素下的链接:
python">from scrapy.http import HtmlResponse
from scrapy.linkextractors import LinkExtractor
html1 = open('LE_Example1.html').read()
response1 = HtmlResponse(url='http://example1.com', body=html1, encoding='utf8')
le = LinkExtractor(restrict_xpaths='//div[@id="top"]')
links = le.extract_links(response1)
print([link.url for link in links])
# 运行结果:
['http://example1.com/intro/install.html', 'http://example1.com/intro/tutorial.html', 'http://example1.com/examples.html']
(6)restrict_css
接收一个 CSS 选择器或一个 CSS 选择器列表,提取 CSS 选择器选中区域下的链接。
例6:提取页面 example1.html
中 <div id="bottom">
元素下 的链接:
python">from scrapy.http import HtmlResponse
from scrapy.linkextractors import LinkExtractor
html1 = open('LE_Example1.html').read()
response1 = HtmlResponse(url='http://example1.com', body=html1, encoding='utf8')
le = LinkExtractor(restrict_css='div#bottom')
links = le.extract_links(response1)
print([link.url for link in links])
# 运行结果:
['http://stackoverflow.com/tags/scrapy/info', 'https://github.com/scrapy/scrapy']
(7)tags
接收一个标签(字符串)或一个标签列表,提取指定标签内的链接,默认为 ['a', 'area']
。
(8)attrs
接收一个属性(字符串)或一个属性列表,提取指定属性内的链接,默认为[‘href’]。
例7:提取页面 example2.html
中引用 JavaScript
文件的链接:
python">from scrapy.http import HtmlResponse
from scrapy.linkextractors import LinkExtractor
html2 = open('LE_Example2.html').read()
response2 = HtmlResponse(url='http://example2.com', body=html2, encoding='utf8')
le = LinkExtractor(tags='script', attrs='src')
links = le.extract_links(response2)
print([link.url for link in links])
# 运行结果:
['http://example2.com/js/app1.js', 'http://example2.com/js/app2.js']
(9)process_value
接收一个形如 func(value)
的回调函数。如果传递了该参数,LinkExtractor 将调用该回调函数对提取的每一个链接(如 a 的 href )进行处理,回调函数正常情况下应返回一个字符串(处理结果),想要抛弃所处理的链接时,返回 None。
例8:在页面 example2.html
中,某些 a
的 href
属性是一段 JavaScript
代码,代码中包含了链接页面的实际 url 地址,此时应对链接进行处理,提取页面 example2.html
中所有实际链接:
python">from scrapy.http import HtmlResponse
from scrapy.linkextractors import LinkExtractor
import re
html2 = open('LE_Example2.html').read()
response2 = HtmlResponse(url='http://example2.com', body=html2, encoding='utf8')
def process(value):
m = re.search("javascript:goToPage\('(.*?)'", value)
# 如果匹配,就提取其中 url 并返回,如果不匹配则返回原值
if m:
value = m.group(1)
return value
le = LinkExtractor(process_value=process)
links = le.extract_links(response2)
print([link.url for link in links])
# 运行结果:
['http://example2.com/doc.html', 'http://example2.com/example.html']
上述文章内容如有错误,谨请各位读者在评论区留言指正!