用scrapy框架做了一個簡單的爬蟲。算是練手。需求:爬取博客園新聞的標題、簡要、發布日期。
打開cmd, 輸入命令:
<code>scrapy shell https://news.cnblogs.com
view(response)</code>
查看元素所在位置。發現title位於h2 class="news_entry"下的a標簽里,如:
可以用css很容易的提取。response.xpath('h2.news_entry a::text').extract_first()
發布日期位於<span class="gray">標簽內,如:
用css也很容易提取。response.css('span.gray::text').extract_first()
但是,提取摘要的時候遇到了一個坑。估計是返爬的一個手法?位於<div class="entry_summary"標簽里。如下圖所示:
貌似很簡單提取,實際上是一個坑。先說一下剛開始的思路。
response.xpath('.//div[@class="entry_summary"]//text()').extract_first()
得到結果如下:
'\n '
.extract_first()的作用是每次提取第一個,放在循環里。這樣可以用yield函數遍歷。我們先用.extract()提取所有的內容,得到了一個列表。也可以不加.extract(),得到一個原始的xpath元素。如下:
發現了問題所在://text()方法是可以提取所有的文本,但是結果是一個列表,不適合做去除空格的進一步動作。而且,空格回車和文本本來應該是一個整體,但是結果卻分開了,以至於用.extract_first()提取到的是空格回車。
網上搜索答案,有人用string()方法可以跨標簽得到文本。用了一下,並沒有什么作用。而且,不能遍歷,只能得到第一個元素。也就是<code>'\n '</code>
貌似無解了?我又繼續網上搜索答案,路人甲的一篇文章給我了思路。他在文章里說,盡量用contains(.),而不是//text()。因為前者會把空格回車和文字作為一個整體提取,屬於模糊查詢,從而可以結合string()方法來獲得文本,也可以結合.strip()去除多余的空格回車。而//text()得到的是一個列表。里面的元素分開了。
如下:
response.xpath('string(.//div[@class="entry_summary"][contains(.,text())])').extract_first().strip()
因為contains()屬於模糊查找,連HTML標簽內容也匹配在內了。所以用string方法去除標簽。然后用.strip()去除空格。
結果相當完美:
附上完整代碼:
import scrapy class NewsSpider(scrapy.Spider): name = "newss" start_urls = ['https://news.cnblogs.com/'] # MAX_DOWNLOAD_NUMB = 100 def parse(self, response): for news in response.css('div.news_block'): title = news.css('h2.news_entry a::text').extract_first() summary = news.xpath('string(.//div[@class="entry_summary"][contains(.,text())])').extract_first().strip() #//*[@id="entry_665993"]/div[2]/div[1]/text() time = news.css('span.gray::text').extract_first() yield { 'title':title, 'summary':summary, 'time':time, } next_url = response.css('div.pager a:last-of-type::attr(href)').extract_first() if next_url: next_url = response.urljoin(next_url) yield scrapy.Request(next_url,callback=self.parse)
執行scrapy crawl newss -o news.csv
得到所有的文章標題、摘要、發布時間列表。