最近一直在學習python,研究了一下爬蟲,也寫了一些demo,所以準備把自己所學分享出來。
一提到python大家第一想法就是爬蟲了吧,確實,python在爬蟲方面提供了各種強大的模塊,再加上python本身語法的簡潔易懂,讓它在爬蟲方面獨樹一幟。
今天要分享的就是用python爬取新浪新聞網站新聞。
這裏要感謝網易雲課堂丘祐瑋老師,本篇博文內容都是基於老師所講內容而寫,想要學習python學習爬蟲的可以去看老師的課程
說在前面:
博主使用的python版本是2.7,版本不一致可能導致一些錯誤;編輯器使用的是jupyter notebook,系統是windows;寫這篇文章主要是爲了分享一下爬取新聞的思路,所以博主不會真的去耗費時間爬取整個站點(有這個想法的可以自己去深入研究)
開始學習之前需要安裝兩個必須的模塊:
requests–是一個很實用的Python HTTP客戶端庫,編寫爬蟲和測試服務器響應數據時經常會用到,BeautifulSoup4–將複雜HTML文檔轉換成一個複雜的樹形結構,每個節點都是Python對象
命令行輸入:
pip install requests
pip install BeautifulSoup4
安裝完後即可開始進行新聞爬取了
請出今天的主角:https://news.sina.com.cn/world/
要爬取的地方如下:
鼠標右鍵打開檢查–network,刷新頁面,通過查看請求信息我們可以看到整個頁面的內容是通過請求https://news.sina.com.cn/world/ 這個鏈接獲取到的
我們抓取新聞無外乎就是新聞標題,來源,發佈時間,編輯,內容等,而我們想要的新聞標題,時間 都是可以通過訪問https://news.sina.com.cn/world/獲得,分析頁面元素我們可以發現我們想要抓取的新聞都有一個共同的class new-item
可以看到文章的發佈時間和鏈接
那麼我們可以這樣寫:
import requests
from bs4 import BeautifulSoup
res = requests.get('https://news.sina.com.cn/world/')#模擬get
請求獲取鏈接返回的內容
res.encoding = 'utf-8'#設置編碼格式爲utf-8
soup = BeautifulSoup(res.text, 'html.parser')#前面已經介紹將html文檔格式化爲一個樹形結構,每個節點都是一個對python對象,方便獲取節點內容
for new in soup.select('.news-item'):#BeautifulSoup提供的方法通過select選擇想要的html節點類名,標籤等,獲取到的內容會被放到列表中
if len(new.select('h2')) > 0:
#加[0]是因爲select獲取內容後是放在list列表中[內容,],text可以獲取標籤中的內容
print new.select('.time')[0].text+' '+new.select('h2')[0].text +' '+ new.select('a')[0]['href']
輸出結果如下:
有了文章標題,鏈接,那麼還需要文章的內容,來源,我們隨便點擊一篇文章進入文章內容頁,分析頁面html標籤
可以看到文章大標題有一個類second-title,文章發佈時間有一個類名date,文章來源有一個類名source,文章內容則是在類名article中被段落標籤p包着,文章編輯則是有一個類名show_author,瞭解了這些我們就可以編寫爬取新聞代碼了
import time
import requests
from bs4 import BeautifulSoup
info = requests.get('http://news.sina.com.cn/w/2018-09-04/doc-ihiixzkm4326136.shtml')
info.encoding = 'utf-8'
html = BeautifulSoup(info.text, 'html.parser')
print html.select('.second-title')[0].text#獲取大標題
print html.select('.date')[0].text#獲取發佈時間
#dt = time.strptime(timesource, '%Y年%m月%d日 %H:%M') #用來格式化時間字符串爲時間格式方便儲存
#dt.strftime('%Y-%m-%d')
print html.select('.source')[0].text + ' '+html.select('.source')[0]['href']
article = []
for v in html.select('.article p')[:-1]:#獲取article中被p包含的內容去除最後一個p標籤即責任編輯
article.append(v.text.strip())#將內容添加到列表中,並去除兩邊特殊字符
author_info = '\n'.join(article)#將列表中內容以換行連接
print author_info
print html.select('.show_author')[0].text.lstrip(u'責任編輯:')#輸出編輯姓名
輸出如下:
這裏面還有一個評論數需要特殊處理才能獲得,查看請求信息發現,評論數是通過ajax異步獲取的如下圖:
請求這個鏈接獲取評論數
注意:由上圖可以看出請求返回的是json格式數據,所以需要引入json模塊進行處理,不過我們可以看到返回的結果左面多出了jsonp_1536240250267(字符,所以要想正確的轉化爲我們要的數據還得引入re模塊進行正則匹配去除掉這個字符串
import json
import re
# comos- hiixzkm4227444爲文章id
comments = requests.get('http://comment5.news.sina.com.cn/page/info?version=1&format=json&channel=gj&newsid=comos-hiixzkm4227444&group=undefined&compress=0&ie=utf-8&oe=utf-8&page=1&page_size=3&t_size=3&h_size=3&thread=1&callback=jsonp_1536041889769&_=1536041889769')
regex = re.compile(r'(.*?)\(')#去除左邊特殊符號
tmp = comments.text.lstrip(regex.search(comments.text).group())
jd = json.loads(tmp.rstrip(')'))#轉換爲字典
print jd['result']['count']['total'] #獲取評論數
經過對比獲取評論的鏈接和文章的鏈接
http://comment5.news.sina.com.cn/page/info?version=1&format=json&channel=gj&newsid=comos-hitesuz3520331
http://news.sina.com.cn/w/2018-09-06/doc-ihitesuz3520331.shtml
我們可以發現comos-後面的字符和doc-i後面的字符一樣,不難猜出這應該就是文章的id了,我們可以通過下面兩個方法獲取文章id(這個很重要)
newsurl = 'http://news.sina.com.cn/o/2015-12-28/doc-ifxmxxsr3837772.shtml'
newsid = newsurl.split('/')[-1].rstrip('.shtml').lstrip('doc-i')
或者
m = re.search('doc-i(.+).shtml', newsurl)
newsid = m.group(1)
現在我們已經能夠獲取文章的各個內容了,那麼如何獲取每一頁的文章呢?
通過點擊分頁我們不難觀察到文章隨着頁碼切換會訪問=1536044408917”>https://interface.sina.cn/news/get_news_by_channel_new_v2018.d.html?cat_1=51923&show_num=27&level=1,2&page=4&callback=newsloadercallback&=1536044408917這個鏈接,懂得一些網站開發的人員都能看出page就是指的當前的頁碼數,page變化訪問的文章列表信息就會變化
現在我們獲取這個頁面的內容
test = requests.get('https://interface.sina.cn/news/get_news_by_channel_new_v2018.d.html?cat_1=51923&show_num=27&level=1,2&page=4&callback=newsloadercallback&_=1536044408917')
test2 = test.text.lstrip('newsloadercallback(')
jd = json.loads(test2.rstrip(')\n'))
for v in jd['result']['data']:
print v['url']
輸出文章的鏈接如下:
現在我們能夠獲得每頁的文章url,同樣也能獲取每篇文章的標題內容,發佈時間,評論數,來源,編輯,那麼剩下的就是把所有代碼組織起來拼出一個可以自動批量爬取文章的爬蟲了,這裏我就不詳細敘述了,直接貼出最終整合版,感興趣的同學可以自己嘗試將上述代碼組織起來
完整代碼如下:
ps:完整代碼中我使用了pandas將爬取的文章輸出到excel表格中
pandas:基於NumPy 的一種工具,該工具是爲了解決數據分析任務而創建的。Pandas 納入了大量庫和一些標準的數據模型,提供了高效地操作大型數據集所需的工具。pandas提供了大量能使我們快速便捷地處理數據的函數和方法。你很快就會發現,它是使Python成爲強大而高效的數據分析環境的重要因素之一
import requests
from bs4 import BeautifulSoup
import time
import json
import re
import pandas
import sys
if sys.getdefaultencoding() != 'utf-8':
reload(sys)
sys.setdefaultencoding('utf-8')
def getnewcontent(url):
result = {}
info = requests.get(url)
info.encoding = 'utf-8'
html = BeautifulSoup(info.text, 'html.parser')
result['title'] = html.select('.second-title')[0].text
result['date'] = html.select('.date')[0].text
result['source'] = html.select('.source')[0].text
article = []
for v in html.select('.article p')[:-1]:
article.append(v.text.strip())
author_info = '\n'.join(article)
result['content'] = author_info
result['author'] = html.select('.show_author')[0].text.lstrip('責任編輯:')
newsid = url.split('/')[-1].rstrip('.shtml').lstrip('doc-i')
commenturl = 'http://comment5.news.sina.com.cn/page/info?version=1&format=json&channel=gj&newsid=comos-{}&group=undefined&compress=0&ie=utf-8&oe=utf-8&page=1&page_size=3&t_size=3&h_size=3&thread=1&callback=jsonp_1536041889769&_=1536041889769'
comments = requests.get(commenturl.format(newsid))
regex = re.compile(r'(.*?)\(')#去除左邊特殊符號
tmp = comments.text.lstrip(regex.search(comments.text).group())
jd = json.loads(tmp.rstrip(')'))
result['comment'] = jd['result']['count']['total'] #獲取評論數
return result
def getnewslink(url):
test = requests.get(url)
test2 = test.text.lstrip('newsloadercallback(')
jd = json.loads(test2.rstrip(')\n'))
content = []
for v in jd['result']['data']:
content.append(getnewcontent(v['url']))
return content
def getdata():
url = 'https://interface.sina.cn/news/get_news_by_channel_new_v2018.d.html?cat_1=51923&show_num=27&level=1,2&page={}&callback=newsloadercallback&_=1536044408917'
weibo_info = []
for i in range(1,3):
newsurl = url.format(i)#字符串格式化用i替換{}
weibo_info.extend(getnewslink(newsurl))
return weibo_info
new_info = getdata()
df = pandas.DataFrame(new_info)
df #去除全部 df.head() 取出5行 head(n) n行
#將文件下載爲excel表格 df.to_excel('weibonews.xlsx')