如何快速爬取新浪新聞並保存到本地

這篇文章能夠快速教你爬取新浪新聞。希望這篇文章對您有所幫助!如果您有想學習的知識或建議,可以給作者留言~



一、爬取場景

1、網頁加載模式

  • 動態網頁

1
動態網頁不同於傳統的靜態網頁,如果想用傳統的方式爬取,會出錯的。
2

  • 靜態網頁

3
上圖爲傳統的靜態網頁。

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"))

4
首先我們看到URL鏈接和得到的結果有重疊的地方,我們可以嘗試精簡下URL鏈接,同時由於格式本身爲”utf-8“我們可以把格式換成”unicode_escape“
5
這樣我們就能得到想要的模塊。

我麼可以用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))

6

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/
7
上圖紅框處,通過進行測試發現:
num控制頁面的整體內容
page顯示的是分頁的內容
我們先進行如下圖的測試8
得到結果如下:
9
這時我們查找ctime的數值並通過時間戳轉換處時間:
10
下面代碼爲重點

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
        

結果如下:
11
12
13


各位路過的朋友,如果覺得可以學到些什麼的話,點個贊再走吧,歡迎各位路過的大佬評論,指正錯誤,也歡迎有問題的小夥伴評論留言,私信。每個小夥伴的關注都是本人更新博客的動力!!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章