網易新聞數據爬取
手動反爬蟲:原博地址
知識梳理不易,請尊重勞動成果,文章僅發佈在CSDN網站上,在其他網站看到該博文均屬於未經作者授權的惡意爬取信息
如若轉載,請標明出處,謝謝!
1. 新建項目
在命令行窗口下輸入scrapy startproject news,如下
然後就自動創建了相應的文件,如下
關於每一個文件的作用,上一個博客上有詳細介紹,可以回頭看一下
2. 修改itmes.py文件
打開scrapy框架自動創建的items.py文件,如下
編寫裏面的代碼,確定我要獲取的信息,比如線程,新聞標題,url,時間,來源,來源的url,新聞的內容等
import scrapy
class NewsItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
news_thread = scrapy.Field()
news_title = scrapy.Field()
news_url = scrapy.Field()
news_time = scrapy.Field()
news_source = scrapy.Field()
source_url = scrapy.Field()
news_body = scrapy.Field()
3. 定義spider,創建一個爬蟲模板
3.1 創建crawl爬蟲模板
在命令行窗口下面 創建一個crawl爬蟲模板,指令執行會在spider文件夾生成一個news163.py文件
注意: 在文件的根目錄下面,指令檢查別輸入錯誤,-t 表示使用後面的crawl模板,news163爲爬蟲文件名稱,最後的news.163.com爲網易新聞域名
然後看一下這個‘crawl’模板和一般的模板有什麼區別,多了鏈接提取器還有一些爬蟲規則,這樣就有利於我們做一些深度信息的自動化爬取
3.2 xpath選擇器
支持xpath和css,其中css選擇器之前的爬蟲案例中介紹過了,這裏是補充xpath的操作,xpath語法如下
① 手寫輸入的:
/html/head/title
定位標題
/html/head/title/text()
提取標題內容
//td
(深度提取的話就是兩個/) 直接定位td標籤
//div[@class='mine']
定義帶有mine屬性的div標籤
② 手動copy的:
直接定位某一具體位置的標籤信息,往往復制粘貼之後使用的是相對路徑,即//
開頭,後面再接標籤相關信息,比如的內容copy之後,粘貼的內容爲://*[@id="js_top_news"]/div[2]/ul/li[2]/a
3.3. 分析網頁內容
在谷歌chrome瀏覽器下,打在網頁新聞的網站,選擇查看源代碼,確認我們可以獲取到itmes.py文件的內容(其實那裏面的要獲取的就是查看了網頁源代碼之後確定可以獲取的)
確認標題、時間、url、來源url和內容可以通過檢查和標籤對應上,比如正文部分
4. 修改spider下創建的爬蟲文件
4.1 導入包
打開創建的爬蟲模板,進行代碼的編寫,除了導入系統自動創建的三個庫,我們還需要導入news.items(這裏就涉及到了包的概念了,最開始說的–init–.py文件存在說明這個文件夾就是一個包可以直接導入,不需要安裝)
注意:使用的類ExampleSpider一定要繼承自CrawlSpider,因爲最開始我們創建的就是一個‘crawl’的爬蟲模板,對應上
import scrapy
from news.items import NewsItem
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
class New163Spider(CrawlSpider):
name = 'new163'
allowed_domains = ['new163.com']
start_urls = ['http://new163.com/']
rules = (
Rule(LinkExtractor(allow=r'/18/04\d+/*'), callback='parse_news', follow=True),
)
def parse_item(self, response):
item = {}
#item['domain_id'] = response.xpath('//input[@id="sid"]/@value').get()
#item['name'] = response.xpath('//div[@id="name"]').get()
#item['description'] = response.xpath('//div[@id="description"]').get()
return item
Rule(LinkExtractor(allow=r’/18/04\d+/*’), callback=‘parse_news’, follow=True),其中第一個allow裏面是書寫正則表達式的(也是我們核心要輸入的內容),第二個是回調函數,第三個表示是否允許深入
4.2 正則表達式的簡單介紹
系統的介紹會在爬蟲專項裏面進行講解,這裏介紹一些基礎性的可以用在這個項目裏面的知識點,,正則表達式是由字符和操作符組成的,常見的語法如下圖
記住一個:“.*?” 惰性匹配,匹配成功一次即可, 幾乎可以解決大部分的問題,還有一些需要我們自己動手編寫
對比新聞的標籤,如下
第一個新聞的url是:“https://news.163.com/20/0204/17/F4IDMINL000189FH.html”
第二個新聞的url是:“https://news.163.com/20/0204/07/F4HC2QS90001899O.html”
…
可以看出規律,因此正則表達式可以按照下面這樣寫
rules = (
Rule(LinkExtractor(allow=r'https://news.163.com/20/0204/\d+/.*?html'), callback='parse_item', follow=True),
)
然後在命令行窗口運行指令:scrapy crawl news163
輸出結果爲:請求返回200,代表請求成功
4.3 回調函數
parse_item是我們要設置的回調函數,先處理兩個較爲簡單的獲取內容,獲取thread(去掉網址的後五個字符的內容)和title(一般就是網頁源代碼的一個title標籤裏的內容),代碼設置如下
def parse_item(self, response):
item = NewsItem()
item['news_thread'] = response.url.strip().split("/")[-1][:-5]
self.get_title(response,item)
return item
def get_title(self,response,item):
title = response.css('title::text').extract()
if title:
print("title:{}".format(title[0]))
item['news_title'] = title[0]
保存後運行命令行窗口,輸出如下
然後再獲取時間,在頁面中選擇檢查,找到新聞時間對應的源代碼中的標籤信息,然後採用css選擇器,找到該標籤信息,如下
獲取新聞時間的代碼如下,time後面的內容就是屬於字符串處理的方式了,目的是爲了獲得正常格式的時間數據
self.get_time(response,item) #這個代碼要放在回調函數裏面
def get_time(self,response,item):
time = response.css('div.post_time_source::text').extract()
if time:
print('time:{}'.format(time[0].strip().replace("來源","").replace('\u3000:',"")))
item['news_time'] = time[0].strip().replace("來源","").replace('\u3000:',"")
輸出結果爲:
接下來獲取新聞來源,查看網頁源代碼,發現新聞來源是存儲id標籤下面,直接就可以進行標籤的查找鎖定(id唯一)
獲取新聞來源的代碼如下
self.get_source(response,item) #這個代碼要放在回調函數裏面
def get_source(self,response,item):
source = response.css("ne_article_source::text").extract()
if source:
print("source:{}".format(source[0]))
item['news_source'] = source[0]
獲取新聞原文URL的方式也是類似,這裏直接給出代碼(注意這裏不是獲取id標籤的文本內容了,而是屬性)
self.get_source_url(response,item)
def get_source_url(self,response,item):
source_url = response.css("ne_article_source::attr(href)").extract()
if source_url:
print("source_url:{}".format(source_url[0]))
item['source_url'] = source_url[0]
獲取新聞內容 ,也是直接給出參考代碼如下
self.get_text(response,item)
def get_text(self,response,item):
text = response.css(".post_text p::text").extract()
if text:
print("text:{}".format(text))
item['news_body'] = text
獲取新聞URL(最初的那個),也是直接給出參考代碼如下
self.get_url(response,item)
def get_url(self,response,item):
url = response.url
if url:
item['news_url'] = url
至此news163.py的全部代碼編寫如下:
import scrapy
from news.items import NewsItem
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
#https://news.163.com/20/0205/06/F4JOHHEJ0001899O.html
#https://news.163.com/20/0205/08/F4JVQU6P000189FH.html
class News163Spider(CrawlSpider):
name = 'news163'
allowed_domains = ['news.163.com']
start_urls = ['http://news.163.com/']
rules = (
Rule(LinkExtractor(allow=r'https://news.163.com/20/0205/\d+/.*?html'), callback='parse_item', follow=True),
)
def parse_item(self, response):
item = NewsItem()
item['news_thread'] = response.url.strip().split("/")[-1][:-5]
self.get_title(response,item)
self.get_time(response,item)
self.get_source(response,item)
self.get_source_url(response,item)
self.get_text(response,item)
self.get_url(response,item)
return item
def get_url(self,response,item):
url = response.url
if url:
item['news_url'] = url
def get_text(self,response,item):
text = response.css(".post_text p::text").extract()
if text:
print("text:{}".format(text))
item['news_body'] = text
def get_source_url(self,response,item):
source_url = response.css("ne_article_source::attr(href)").extract()
if source_url:
#print("source_url:{}".format(source_url[0]))
item['source_url'] = source_url[0]
def get_source(self,response,item):
source = response.css("ne_article_source::text").extract()
if source:
print("source:{}".format(source[0]))
item['news_source'] = source[0]
def get_time(self,response,item):
time = response.css('div.post_time_source::text').extract()
if time:
print('time:{}'.format(time[0].strip().replace("來源","").replace('\u3000:',"")))
item['news_time'] = time[0].strip().replace("來源","").replace('\u3000:',"")
def get_title(self,response,item):
title = response.css('title::text').extract()
if title:
print("title:{}".format(title[0]))
item['news_title'] = title[0]
保存後,運行命令行輸出如下( 注意、注意、注意, 在調試的過程中不要頻繁的運行這個指令,否則會導致服務器無法訪問),只截取部分輸出結果
5. 修改pipeline文件下的內容
5.1 導入csv文件儲存包
要將數據儲存在本地,需要以一種格式作爲儲存的條件,逗號分隔符(csv)文件就可以滿足這種要求,而且也是現在主要存儲數據的工具
from scrapy.exporters import CsvItemExporter
5.2 定義進程函數
首先從初始化函數,包含了創建收集數據的文件和項目啓動器
def __init__(self):
self.file = open('news_data.csv', 'wb')
self.exporter = CsvItemExporter(self.file, encoding = 'utf-8')
self.exporter.start_exporting()
其次,定義爬蟲結束器,進行項目的收尾工作,把進程和文件都關閉掉,防止內存溢出
def close_spider(self,spider):
self.exporter.finish_exporting()
self.file.close()
最後在處理函數裏面,開啓導入,最後返回Item
def process_item(self, item, spider):
self.exporter.export_item(item)
return item
至此,pipeline裏的代碼編寫就已經完成了,這時候就要在setting.py文件裏面進開啓pipeline通道,取消如下內容的註釋,如下
最後整個pipeline.py的文件代碼如下,注意檢查縮進的問題(Sublime編輯器裏面有講縮進全部轉換成爲tab格式的選項,確保縮進一致,而且還是要注意一下網頁編碼的問題,否則會出現亂碼的情況,encoding要根據爬取網頁的編碼格式設定)
from scrapy.exporters import CsvItemExporter
class NewsPipeline(object):
def __init__(self):
self.file = open('news_data.csv', 'wb')
self.exporter = CsvItemExporter(self.file, encoding = 'gbk')
self.exporter.start_exporting()
def close_spider(self,spider):
self.exporter.finish_exporting()
self.file.close()
def process_item(self, item, spider):
self.exporter.export_item(item)
return item
6. 運行結果
最後在命令行窗口,運行指令,在窗口界面出現爬取內容輸出的同時,在news文件夾下也自動生成了news_data.csv文件,如下
news_data.csv文件中數據樣式如下,至此整個利用Scrapy爬取網頁新聞的項目就全部完結了