這篇文章能夠快速教你爬取新浪新聞。希望這篇文章對您有所幫助!如果您有想學習的知識或建議,可以給作者留言~
如何快速爬取新浪新聞並保存到本地
- 此爲一個系列,並將持續更新:
專欄鏈接:快速入門之爬蟲
一、爬取場景
1、網頁加載模式
- 動態網頁
動態網頁不同於傳統的靜態網頁,如果想用傳統的方式爬取,會出錯的。
- 靜態網頁
上圖爲傳統的靜態網頁。
2、網頁結構
列表頁-詳情頁
API遍歷
二、API遍歷方法爬取新聞
1、找到API
- 使用開發人員工具-network模塊找到API
- API:https://feed.mix.sina.com.cn/api/roll/get?pageid=153&lid=2509&k=&num=50&page=1&r=0.7210316507361691&callback=jQuery1112011987950839178185_1585738993071&_=1585738993083
2、分析關鍵API參數
- Pageid:應該是新浪API列表列表的分配參數
- Num:每頁的新聞數。可以通過修改這個參數
- Page:新聞列表的翻頁數。可以通過修改這個參數來遍歷
3、根據API參數設計爬蟲邏輯
- 先使用APII遍歷新聞URL
- 然後根據新聞URL獲取詳細頁,抽取我們想要的數據
4、測試使用
三、代碼實現
1、嘗試獲取動態網頁
import requests
print(requests.get("https://feed.mix.sina.com.cn/api/roll/get?pageid=153&lid=2509&k=&num=50&page=1&r=0.7210316507361691&callback=jQuery1112011987950839178185_1585738993071&_=1585738993083").content.decode("utf-8"))
首先我們看到URL鏈接和得到的結果有重疊的地方,我們可以嘗試精簡下URL鏈接,同時由於格式本身爲”utf-8“
我們可以把格式換成”unicode_escape“
這樣我們就能得到想要的模塊。
我麼可以用json.loads()
獲取字符串
import requests
import json
print(json.loads(requests.get("https://feed.mix.sina.com.cn/api/roll/get?pageid=153&lid=2509&k=&num=50&page=1&r=0.7210316507361691").content))
2、觀察URL
https://feed.mix.sina.com.cn/api/roll/get?pageid=153&lid=2509&k=&num=50&page=1&r=0.7210316507361691
在這時我們可能會用到時間戳這樣一個在線工具:
地址:https://tool.lu/timestamp/
上圖紅框處,通過進行測試發現:
num
控制頁面的整體內容
page
顯示的是分頁的內容
我們先進行如下圖的測試
得到結果如下:
這時我們查找ctime的數值並通過時間戳轉換處時間:
下面代碼爲重點
3、程序
3.1、導入所需要的包
#導入所需要的包
import codecs #用來存儲爬取到的信息
from pybloom_live import ScalableBloomFilter # 用於URL去重的
import requests #用於發起請求,獲取網頁信息
import json #處理json格式的數據
from bs4 import BeautifulSoup as bs #用於數據抽取
from lxml import etree #用於數據抽取
import re #正則語言類庫
3.2、編寫抽取模塊
1、使用BeautifulSoup,編寫抽取模塊 編寫一個函數,函數功能是通過傳入的URL參數,利用BeautifulSoup獲取詳情頁面中的新聞標題、內容、來源、時間等信息。
#定義一個函數,函數功能是通過傳入的URL參數,獲取詳情頁面中的新聞標題、內容、來源、時間等信息。
#函數名稱:getdetailpagebybs ;所需參數:URL
def getdetailpagebybs(url):
detail = {} #創建一個字典,存放URL、title、newstime等信息
detail["url"] = url #將URL時間存入detail字典中的相應鍵值中
page=requests.get(url).content #使用requests.get方法獲取網頁代碼,由於bs4可以自動解碼URL的編碼,所以此處不需要decode
html=bs(page, "lxml") #使用lxml解析器
title=html.find(class_="main-title") #獲取新聞網頁中的title信息,此處網頁中只有一個“class=main-title”,所以使用find即可
print(title.text) #展示新聞標題
detail["title"] = title.text #將新聞標題以文本形式存入detail字典中的相應鍵值中
artibody=html.find(class_="article") #使用find方法,獲取新聞網頁中的article信息
#print(artibody.text)
detail["artibody"]=artibody.text#。。。。。。。
date_source = html.find(class_="date-source") #使用find方法,獲取新聞網頁中的date-source信息
#由於不同的新聞詳情頁之間使用了不同的標籤元素,直接抽取可能會報錯,所以此處使用判斷語句來進行區分爬取
if date_source.a: #判斷date-source節點中是否包含有'a'元素
#print(date_source.span.text)
detail["newstime"]=date_source.span.text #抽取'span'標籤中時間信息
#print(date_source.a.text)
detail["newsfrom"] =date_source.a.text #抽取'a'標籤中新聞來源信息
else:
#print(date_source("span")[0].text)
detail["newstime"] =date_source("span")[0].text #抽取'span'標籤中包含的時間信息
#print(date_source("span")[1].text)
detail["newsfrom"] =date_source("span")[1].text#抽取'span'標籤中包含的新聞來源信息
#也可以使用正則表達式來抽取信息
#print(date_source.prettify())
r=re.compile("(\d{4}年\d{2}月\d{2}日 \d{2}:\d{2})") #編寫時間信息的正則表達式
re_newstime=r.findall(date_source.text) #使用findall方法,按照編寫的正則語句,從date_source節點中所包含的時間信息
detail["re_newstime"] =re_newstime.text #將新聞時間存入detail字典中的相應鍵值中
return detail #函數返回值爲存放抽取信息的字典
2、使用lxml,編寫抽取模塊 編寫一個函數,使用lxml進行抽取的模塊,使用xpath方法,來抽取詳情頁面中的新聞標題、內容、來源、時間等信息。
#定義一個函數,函數功能是通過傳入的URL參數,獲取詳情頁面中的新聞標題、內容、來源、時間等信息。
#函數名稱:getdetailpagebylxml ;所需參數:URL
def getdetailpagebylxml(url):
detail={} #創建一個字典,存放URL、title、newstime等信息
detail["url"]=url #將URL存入字典中的相應鍵值中
page = requests.get(url).content.decode("utf-8") #獲取網頁源代碼,並使用utf-8編碼
#由於網頁的結構可能會隨網站更新等原因發生變化,使用xpath方法抽取信息時,從網頁複製元素的xpath可能已無法直接使用
#如本例中從網頁中複製的date-source元素的xpath爲“//*[@id="top_bar"]/div/div[2]”,按照直接複製的xpath將無法正常獲取元素信息
#需要人爲的修改調整,調整爲"//div[@class=\"date-source\"後可以正常獲取元素信息了
html = etree.HTML(page)
title = html.xpath("/html/head/title/text()")[0] #使用xpath方法抽取title信息
detail["title"]=title #將標題存入字典中的相應鍵值中
print(title)
artibody = html.xpath("//div[@class=\"article\"]/p//text()") #使用xpath方法抽取article信息
detail["artibody"]=''.join(artibody) # artibody是原本一個list對象,使用join方法將其連接起來
date_source = html.xpath("//div[@class=\"date-source\"]//text()")
detail["newstime"]=date_source[1]
detail["newsfrom"]=date_source[3]
#也可以使用正則表達式來抽取信息,解析同2.1的函數中正則模塊
#print(date_source.prettify())
r=re.compile("(\d{4}年\d{2}月\d{2}日 \d{2}:\d{2})")
re_newstime=r.findall(date_source.text)
detail["re_newstime"] =re_newstime.text
return detail
3.3、編寫存儲模塊
編寫一個函數,使用codecs包,將抽取後的信息存入到指定位置的文件中
#函數名稱:savenews; 所需參數:data(要保存的數據),new(存入的文件名稱)
def savenews(data,new):
fp = codecs.open('.\\sinaNews\\'+new+'.txt', 'a+', 'utf-8')
fp.write(json.dumps(data, ensure_ascii=False))
fp.close()
3.4、編寫爬蟲主題
利用以上編寫好的兩個模塊,運行爬蟲
1、設置全局變量
#初始化全局的變量
#使用ScalableBloomFilter模塊,對獲取的URL進行去重處理
urlbloomfilter=ScalableBloomFilter(initial_capacity=100, error_rate=0.001, mode=ScalableBloomFilter.LARGE_SET_GROWTH)
page = 1 #設置爬蟲初始爬取的頁碼
error_url=set() #創建集合,用於存放出錯的URL鏈接
2、獲取URL
- 獲取URL 由於本案例中的新浪新聞網頁
是動態網頁,所以不能直接對此網頁進行爬取。需要通過開發者工具,查看該網頁的NetWork,找到該網頁的API接口URL,並以此作爲初始URL進行爬取。通過拼接參數’page’來控制爬取頁數。 - 使用循環控制爬蟲,並調用之前編寫好的抽取模塊和存儲模塊,運行爬蟲
1、使用BeautifulSoup抽取模塊和存儲模塊
#使用BeautifulSoup抽取模塊和存儲模塊
#設置爬取頁面的上限,由於僅用於案例展示,所以此處只爬取前一頁的新聞數據
while page <= 1:
#以API爲index開始獲取url列表
data = requests.get("https://feed.mix.sina.com.cn/api/roll/get?pageid=153&lid=2509&k=&num=50&page="+str(page)) #拼接URL,並獲取索引頁面信息
if data.status_code == 200: #當請求頁面返回200(代表正確)時,獲取網頁數據
#將獲取的數據json化
data_json = json.loads(data.content)
news=data_json.get("result").get("data") #獲取result節點下data節點中的數據,此數據爲新聞詳情頁的信息
#從新聞詳情頁信息列表news中,使用for循環遍歷每一個新聞詳情頁的信息
for new in news:
# 查重,從new中提取URL,並利用ScalableBloomFilter查重
if new["url"] not in urlbloomfilter:
urlbloomfilter.add(new["url"]) #將爬取過的URL放入urlbloomfilter中
try:
#print(new)
#抽取模塊使用bs4
detail = getdetailpagebybs(new["url"])
#存儲模塊 保存到txt
#savenews(detail,new['docid'][-7:])#################################################################
except Exception as e:
error_url.add(new["url"]) #將未能正常爬取的URL存入到集合error_url中
page+=1 #頁碼自加1
2、使用lxml抽取模塊和存儲模塊
while page <= 1:
#以API爲index開始獲取url列表
data = requests.get("https://feed.mix.sina.com.cn/api/roll/get?pageid=153&lid=2509&k=&num=50&page="+str(page)) #拼接URL,並獲取索引頁面信息
if data.status_code == 200: #當請求頁面返回200(代表正確)時,獲取網頁數據
#將獲取的數據json化
data_json = json.loads(data.content)
news=data_json.get("result").get("data") #獲取result節點下data節點中的數據,此數據爲新聞詳情頁的信息
#從新聞詳情頁信息列表news中,使用for循環遍歷每一個新聞詳情頁的信息
for new in news:
# 查重,從new中提取URL,並利用ScalableBloomFilter查重
if new["url"] not in urlbloomfilter:
urlbloomfilter.add(new["url"]) #將爬取過的URL放入urlbloomfilter中
try:
#print(new)
#抽取模塊使用lxml
detail=getdetailpagebylxml(new["url"])
#存儲模塊 保存到txt
#savenews(detail,new['docid'][-7:])#######################################################
except Exception as e:
error_url.add(new["url"]) #將未能正常爬取的URL存入到集合error_url中
page+=1 #頁碼自加1
四、完整代碼
import codecs #用來存儲爬取到的信息
from pybloom_live import ScalableBloomFilter # 用於URL去重的
import requests #用於發起請求,獲取網頁信息
import json #處理json格式的數據
from bs4 import BeautifulSoup as bs #用於數據抽取
import re #正則語言類庫
def getdetailpagebybs(url):
detail = {} # 創建一個字典,存放URL、title、newstime等信息
detail["url"] = url # 將URL時間存入detail字典中的相應鍵值中
page = requests.get(url).content # 使用requests.get方法獲取網頁代碼,由於bs4可以自動解碼URL的編碼,所以此處不需要decode
html = bs(page, "lxml") # 使用lxml解析器
title = html.find(class_="main-title") # 獲取新聞網頁中的title信息,此處網頁中只有一個“class=main-title”,所以使用find即可
print(title.text) # 展示新聞標題
detail["title"] = title.text # 將新聞標題以文本形式存入detail字典中的相應鍵值中
artibody = html.find(class_="article") # 使用find方法,獲取新聞網頁中的article信息
print(artibody.text)
detail["artibody"] = artibody.text # 。。。。。。。
date_source = html.find(class_="date-source") # 使用find方法,獲取新聞網頁中的date-source信息
# 由於不同的新聞詳情頁之間使用了不同的標籤元素,直接抽取可能會報錯,所以此處使用判斷語句來進行區分爬取
if date_source.a: # 判斷date-source節點中是否包含有'a'元素
print(date_source.span.text)
detail["newstime"] = date_source.span.text # 抽取'span'標籤中時間信息
print(date_source.a.text)
detail["newsfrom"] = date_source.a.text # 抽取'a'標籤中新聞來源信息
else:
print(date_source("span")[0].text)
detail["newstime"] = date_source("span")[0].text # 抽取'span'標籤中包含的時間信息
print(date_source("span")[1].text)
detail["newsfrom"] = date_source("span")[1].text # 抽取'span'標籤中包含的新聞來源信息
return detail # 函數返回值爲存放抽取信息的字典
def savenews(data,new):
fp = codecs.open('D:/sinaNews/'+new+'.txt', 'a+', 'utf-8')
fp.write(json.dumps(data, ensure_ascii=False))
fp.close()
#使用ScalableBloomFilter模塊,對獲取的URL進行去重處理
urlbloomfilter=ScalableBloomFilter(initial_capacity=100, error_rate=0.001, mode=ScalableBloomFilter.LARGE_SET_GROWTH)
page = 1 #設置爬蟲初始爬取的頁碼
error_url=set() #創建集合,用於存放出錯的URL鏈接
#使用BeautifulSoup抽取模塊和存儲模塊
#設置爬取頁面的上限,
while page <= 10:
#以API爲index開始獲取url列表
data = requests.get("https://feed.mix.sina.com.cn/api/roll/get?pageid=153&lid=2509&k=&num=50&page="+str(page)) #拼接URL,並獲取索引頁面信息
if data.status_code == 200: #當請求頁面返回200(代表正確)時,獲取網頁數據
#將獲取的數據json化
data_json = json.loads(data.content)
news=data_json.get("result").get("data") #獲取result節點下data節點中的數據,此數據爲新聞詳情頁的信息
#從新聞詳情頁信息列表news中,使用for循環遍歷每一個新聞詳情頁的信息
for new in news:
# 查重,從new中提取URL,並利用ScalableBloomFilter查重
if new["url"] not in urlbloomfilter:
urlbloomfilter.add(new["url"]) #將爬取過的URL放入urlbloomfilter中
try:
print(new)
#抽取模塊使用bs4
detail = getdetailpagebybs(new["url"])
#存儲模塊 保存到txt
savenews(detail,new['docid'][-7:])#################################################################
except Exception as e:
error_url.add(new["url"]) #將未能正常爬取的URL存入到集合error_url中
page+=1 #頁碼自加1
結果如下:
各位路過的朋友,如果覺得可以學到些什麼的話,點個贊再走吧,歡迎各位路過的大佬評論,指正錯誤,也歡迎有問題的小夥伴評論留言,私信。每個小夥伴的關注都是本人更新博客的動力!!!