此节将会独立于刚新建的项目,学习 Scrapy shell
,掌握获取数据的基本方法
何为数据
在开发过程中,我们需要获取站点的信息,这些信息包括站点 url、响应状态码、header、body…… 在 Scrapy
中,Response 相当于一个数据字典,包含着站点的大部分信息,在请求后将 Response 作为参数传给了 parse 回调函数。
获取数据
CSS 选择器
quotes-1.html 和 quotes-2.html 文件只是将站点页面保存了下来,实际开发并不会这样做,而是会在此基础上提取我们想要的数据。学习 Scrapy
最有效的方法其实是使用其提供的 Scrapy shell。通过使用 shell
,我们可以更好地理解 Scrapy
爬取数据的原理和机制,并且快速掌握提取数据的方法。还是使用 https://quotes.toscrape.com 做演示。
1
| scrapy shell 'https://quotes.toscrape.com/page/1/'
|
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 41 42 43 44 45 46 47 48 49 50 51 52 53
| 2022-09-16 23:13:13 [scrapy.utils.log] INFO: Scrapy 2.6.2 started (bot: jd) 2022-09-16 23:13:13 [scrapy.utils.log] INFO: Versions: lxml 4.9.1.0, libxml2 2.9.12, cssselect 1.1.0, parsel 1.6.0, w3lib 2.0.1, Twisted 22.8.0, Python 3.9.12 (tags/v3.9.12:b28265d, Mar 23 2022, 23:52:46) [MSC v.1929 64 bit (AMD64)], pyOpenSSL 22.0.0 (OpenSSL 3.0.5 5 Jul 2022), cryptography 38.0.1, Platform Windows-10-10.0.25201-SP0 2022-09-16 23:13:13 [scrapy.crawler] INFO: Overridden settings: {'BOT_NAME': 'jd', 'DUPEFILTER_CLASS': 'scrapy.dupefilters.BaseDupeFilter', 'LOGSTATS_INTERVAL': 0, 'NEWSPIDER_MODULE': 'jd.spiders', 'ROBOTSTXT_OBEY': True, 'SPIDER_MODULES': ['jd.spiders']} 2022-09-16 23:13:13 [scrapy.utils.log] DEBUG: Using reactor: twisted.internet.selectreactor.SelectReactor 2022-09-16 23:13:13 [scrapy.extensions.telnet] INFO: Telnet Password: 5740caa42eea4fff 2022-09-16 23:13:13 [scrapy.middleware] INFO: Enabled extensions: ['scrapy.extensions.corestats.CoreStats', 'scrapy.extensions.telnet.TelnetConsole'] 2022-09-16 23:13:13 [scrapy.middleware] INFO: Enabled downloader middlewares: ['scrapy.downloadermiddlewares.robotstxt.RobotsTxtMiddleware', 'scrapy.downloadermiddlewares.httpauth.HttpAuthMiddleware', 'scrapy.downloadermiddlewares.downloadtimeout.DownloadTimeoutMiddleware', 'scrapy.downloadermiddlewares.defaultheaders.DefaultHeadersMiddleware', 'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware', 'scrapy.downloadermiddlewares.retry.RetryMiddleware', 'scrapy.downloadermiddlewares.redirect.MetaRefreshMiddleware', 'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware', 'scrapy.downloadermiddlewares.redirect.RedirectMiddleware', 'scrapy.downloadermiddlewares.cookies.CookiesMiddleware', 'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware', 'scrapy.downloadermiddlewares.stats.DownloaderStats'] 2022-09-16 23:13:13 [scrapy.middleware] INFO: Enabled spider middlewares: ['scrapy.spidermiddlewares.httperror.HttpErrorMiddleware', 'scrapy.spidermiddlewares.offsite.OffsiteMiddleware', 'scrapy.spidermiddlewares.referer.RefererMiddleware', 'scrapy.spidermiddlewares.urllength.UrlLengthMiddleware', 'scrapy.spidermiddlewares.depth.DepthMiddleware'] 2022-09-16 23:13:13 [scrapy.middleware] INFO: Enabled item pipelines: [] 2022-09-16 23:13:13 [scrapy.extensions.telnet] INFO: Telnet console listening on 127.0.0.1:6023 2022-09-16 23:13:13 [scrapy.core.engine] INFO: Spider opened 2022-09-16 23:13:15 [scrapy.core.engine] DEBUG: Crawled (404) <GET https://quotes.toscrape.com/robots.txt> (referer: None) 2022-09-16 23:13:15 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://quotes.toscrape.com/page/1/> (referer: None) [s] Available Scrapy objects: [s] scrapy scrapy module (contains scrapy.Request, scrapy.Selector, etc) [s] crawler <scrapy.crawler.Crawler object at 0x000001BEF5750490> [s] item {} [s] request <GET https://quotes.toscrape.com/page/1/> [s] response <200 https://quotes.toscrape.com/page/1/> [s] settings <scrapy.settings.Settings object at 0x000001BEF5750760> [s] spider <DefaultSpider 'default' at 0x1bef5b711c0> [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 >>>
|
通过此命令其实已经完成了爬取的过程。并启动了 shell
模式,光标闪烁接收我们的输出,我们在 response 中获取到想要的数据,比如:
1 2
| >>> response.css('title') [<Selector xpath='descendant-or-self::title' data='<title>Quotes to Scrape</title>'>]
|
获取名为 title
元素的信息,并返回一个选择器列表,这些选择器含有 xpath
和 data
参数,允许进一步操作。可以获取其文本数据,getall()
会遍历选择器列表返回一个内容列表:
1 2
| >>> response.css('title::text').getall() ['Quotes to Scrape']
|
这里需要注意::text
和 getall()
。
::text
在 CSS 选择器中添加此查询条件,这就意味着我们想要获取 title
标签的文本元素,如果不添加此查询条件,将会返回整个标签元素。
1 2
| >>> response.css('title').getall() ['<title>Quotes to Scrape</title>']
|
getall()
顾名思义,这个函数会遍历选择器列表并返回一个列表结果。如果只想要第一个结果,可以使用 get()
函数:
1 2
| >>> response.css('title::text').get() 'Quotes to Scrape'
|
除了 getall()
和 get()
函数,还可以利用正则匹配获取数据。
1 2 3 4 5 6
| >>> response.css('title::text').re(r'Quotes.*') ['Quotes to Scrape'] >>> response.css('title::text').re(r'Q\w+') ['Quotes'] >>> response.css('title::text').re(r'(\w+) to (\w+)') ['Quotes', 'Scrape']
|
XPath 选择器
Scrapy
还支持了 XPath 选择器来获取数据。
1 2 3 4
| >>> response.xpath('//title') [<Selector xpath='//title' data='<title>Quotes to Scrape</title>'>] >>> response.xpath('//title/text()').get() 'Quotes to Scrape'
|
XPath 表达式十分强大,奠定了 Scrapy 选择器的基础。事实上,CSS 选择器在执行时 Scrapy
会将其转换为 XPath 选择器。仔细看 CSS 选择器的返回值你就会发现这一点。
虽然 XPath 表达式可能没有 CSS 选择器那么流行,但它提供了更强大的功能,因为除了导航结构之外,它还可以查看内容。使用 XPath,可以选择以下内容:选择包含文本 “下一页” 的链接。这使得 XPath 非常适合于抓取任务,即使我们已经知道如何构造 CSS 选择器,XPath 也将使抓取更容易。
大展身手
介绍了两个利器,让我们回到爬虫。直到现在,shell 环境下并没有提取指定的数据,仅仅只是在保存的页面文件中获取数据。可以将这些逻辑代码集成到新建的爬虫文件中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import scrapy
class QuotesSpider(scrapy.Spider): name = "quotes" start_urls = [ 'https://quotes.toscrape.com/page/1/', 'https://quotes.toscrape.com/page/2/', ]
def parse(self, response): for quote in response.css('div.quote'): yield { 'text': quote.css('span.text::text').get(), 'author': quote.css('small.author::text').get(), 'tags': quote.css('div.tags a.tag::text').getall(), }
|
是不是很熟悉,这就是第一节中的代码逻辑,不再赘述。
页面跳转
在实际开发过程中,往往需要在爬虫中跳转页面。当然将网站链接写到 start_urls
列表中固然可以,但是对于大量的页面跳转,将每个链接都写入其中就会变得很麻烦,缺乏执行性。Scrapy
中的 response 提供了 urljoin()
和 follow()
方法来应对这种问题。
总结
熟悉了 Scrapy 的两种选择器:CSS选择器
和 XPath选择器
,学习了获取数据的方法。