自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

如何用 100 行 Python 代碼實現(xiàn)新聞爬蟲?這樣可算成功?

開發(fā) 后端
每天我都要坐地鐵上班,而地鐵里信號差。但我希望在坐地鐵的時候讀些新聞,于是就寫了下面這個新聞爬蟲。我并沒有打算做很漂亮的應(yīng)用,所以只完成了原型,它可以滿足我最基本的需求。其思路很簡單。

每天我都要坐地鐵上班,而地鐵里信號差。但我希望在坐地鐵的時候讀些新聞,于是就寫了下面這個新聞爬蟲。我并沒有打算做很漂亮的應(yīng)用,所以只完成了原型,它可以滿足我最基本的需求。其思路很簡單:

找到新聞源;用Python抓取新聞;利用BeautifulSoup分析HTML并提取出內(nèi)容;轉(zhuǎn)換成容易閱讀的格式并通過郵件發(fā)送。

下面詳細介紹每個部分的實現(xiàn)。

新聞源:Reddit

我們可以通過Reddit提交新聞鏈接并為之投票,因此Reddit是個很好的新聞來源。但接下來的問題是:怎樣才能獲取每天最流行的新聞?在考慮抓取之前,我們應(yīng)該先考慮目標網(wǎng)站有沒有提供API。因為使用API完全合法,更重要的是它能提供機器可讀的數(shù)據(jù),這樣就無需再分析HTML了。幸運的是Reddit提供了API。我們可以從API列表中找到所需的功能:/top。該功能可以返回Reddit或指定subreddit上最流行的新聞。接下來的問題是:這個API怎么用?仔細閱讀了Reddit的文檔之后,我找到了最有效的用法。第一步:在Reddit上創(chuàng)建一個應(yīng)用。登錄之后前往“preferences → apps”頁面,底部有個名為“create another app...”的按鈕。點擊后創(chuàng)建一個“script”類型的應(yīng)用。我們不需要提供“about url”或“redirect url”,因為這個應(yīng)用不對公眾開放,也不會被別人使用。

 

如何用 100 行 Python 代碼實現(xiàn)新聞爬蟲?這樣可算成功?

應(yīng)用創(chuàng)建之后,可以在應(yīng)用信息里找到App ID和Secret。

 

如何用 100 行 Python 代碼實現(xiàn)新聞爬蟲?這樣可算成功?

下個問題是如何使用App ID和Secret。由于我們只需獲取指定SubReddit上最流行的新聞,而無需訪問任何與用戶相關(guān)的信息,所以理論上來說我們無需提供用戶名或密碼之類的個人信息。Reddit提供了“Application Only OAuth”(https://github.com/reddit-archive/reddit/wiki/OAuth2#application-only-oauth)的形式,通過這種方式,應(yīng)用可以匿名訪問公開的信息。運行下面這條命令:

  1. $ curl -X POST -H 'User-Agent: myawesomeapp/1.0' -d grant_type=client_credentials --user 'OUR_CLIENT_ID:OUR_CLIENT_SECRET'  https://www.reddit.com/api/v1/access_token 

該命令會返回access token:

  1. {"access_token""ABCDEFabcdef0123456789""token_type""bearer""expires_in": 3600, "scope""*"

太好了!有了access token之后就可以大展拳腳了。最后,如果不想自己寫API的訪問代碼的話,可以使用Python客戶端:https://github.com/praw-dev/praw先做一下測試,從/r/Python獲取最流行的5條新聞:

  1. >>> import praw 
  2. >>> import pprint 
  3. >>> reddit = praw.Reddit(client_id='OUR_CLIENT_ID'
  4. ...                      client_secret='OUR_SECRET'
  5. ...                      grant_type='client_credentials'
  6. ...                      user_agent='mytestscript/1.0'
  7. >>> subs = reddit.subreddit('Python').top(limit=5) 
  8. >>> pprint.pprint([(s.score, s.title) for s in subs]) 
  9. [(6555, 'Automate the boring stuff with python - tinder'), 
  10.  (4548, 
  11.   'MS is considering official Python integration with Excel, and is asking for ' 
  12.   'input'), 
  13.  (4102, 'Python Cheet Sheet for begineers'), 
  14.  (3285, 
  15.   'We started late, but we managed to leave Python footprint on r/place!'), 
  16.  (2899, "Python Section at Foyle's, London")] 

成功了!

抓取新聞頁面

下一步的任務(wù)是抓取新聞頁面,這其實很簡單。通過上一步我們可以得到Submission對象,其URL屬性就是新聞的地址。我們還可以通過domain屬性過濾掉那些屬于Reddit自己的URL:

  1. subs = [sub for sub in subs if not sub.domain.startswith('self.')] 

我們只需要抓取該URL即可,用Requests很容易就可以做到:

  1. for sub in subs: 
  2.   res = requests.get(sub.url) 
  3.   if (res.status_code == 200 and 'content-type' in res.headers and 
  4.       res.headers.get('content-type').startswith('text/html')): 
  5.     html = res.text 

這里我們略過了content type不是text/html的新聞地址,因為Reddit的用戶有可能會提交直接指向圖片的鏈接,我們不需要這種。

提取新聞內(nèi)容

下一步是從HTML中提取內(nèi)容。我們的目標是提取新聞的標題和正文,而且可以忽略其他不需要閱讀的內(nèi)容,如頁首、頁腳、側(cè)邊欄等。這項工作很難,其實并沒有通用的完美解決辦法。雖然BeautifulSoup可以幫我們提取文本內(nèi)容,但它會連頁首頁腳一起提取出來。不過幸運的是,我發(fā)現(xiàn)目前網(wǎng)站的結(jié)構(gòu)比以前好很多。沒有表格布局,也沒有和<font>和<br>,整個文章頁面清晰地用<h1>和<p>標出了標題和每個段落。而且絕大部分網(wǎng)站會把標題和正文放在同一個容器元素中,比如像這樣:

 

  1. <header>Site Navigation</header> 
  2. <div id="#main"
  3.   <section
  4.     <h1 class="title">Page Title</h1> 
  5.   </section
  6.   <section
  7.     <p>Paragraph 1</p> 
  8.     <p>Paragraph 2</p> 
  9.   </section
  10. </div> 
  11. <aside>Sidebar</aside> 
  12. <footer>Copyright...</footer> 

這個例子中頂層的<div id="#main">就是用于標題和正文的容器。所以可以利用如下算法找到正文:

  • 找到<h1>作為標題。出于SEO的目的,通常頁面上只會有一個<h1>;
  • 找到<h1>的父元素,檢查該父元素是否包含足夠多的<p>;
  • 重復(fù)第2步,直到找到一個包含足夠多<p>的父元素,或到達<body>元素。如果找到了包含足夠<p>的父元素,則該父元素就是正文的容器。如果在找到足夠的<p>之前遇到了<body>,說明頁面不包含任何可供閱讀的內(nèi)容。

這個算法雖然非常簡陋,并沒有考慮任何語義信息,但完全行得通。畢竟,算法運行失敗時只需要忽略掉那篇文章就行了,少讀一篇文章沒什么大不了的……當然你可以通過解析<header>、<footer>或#main、.sidebar等語義元素來實現(xiàn)更準確的算法。用這個算法可以很容易地寫出解析代碼:

 

  1. soup = BeautifulSoup(text, 'html.parser'
  2. # find the article title 
  3. h1 = soup.body.find('h1'
  4. # find the common parent for <h1> and all <p>s. 
  5. root = h1 
  6. while root.name != 'body' and len(root.find_all('p')) < 5: 
  7.   root = root.parent 
  8. if len(root.find_all('p')) < 5: 
  9.   return None 
  10. # find all the content elements. 
  11. ps = root.find_all(['h2''h3''h4''h5''h6''p''pre']) 

這里我利用len(root.find_all('p')) < 5作為正文過濾的條件,因為真正的新聞不太可能少于5個段落。大家可以根據(jù)需要調(diào)整這個值。

轉(zhuǎn)換成易于閱讀的格式

最后一步是將提取出的內(nèi)容轉(zhuǎn)換為易于閱讀的格式。我選擇了Markdown,不過你可以寫出更好的轉(zhuǎn)換器。本例中我只提取了和<h#>和<p>、<pre>,所以簡單的函數(shù)就能滿足要求:

  1. ps = root.find_all(['h2''h3''h4''h5''h6''p''pre']) 
  2. ps.insert(0, h1)    # add the title 
  3. content = [tag2md(p) for p in ps] 
  4. def tag2md(tag): 
  5.   if tag.name == 'p'
  6.     return tag.text 
  7.   elif tag.name == 'h1'
  8.     return f'{tag.text}\n{"=" * len(tag.text)}' 
  9.   elif tag.name == 'h2'
  10.     return f'{tag.text}\n{"-" * len(tag.text)}' 
  11.   elif tag.name in ['h3''h4''h5''h6']: 
  12.     return f'{"#" * int(tag.name[1:])} {tag.text}' 
  13.   elif tag.name == 'pre'
  14.     return f'```\n{tag.text}\n```' 

完整的代碼

我在Github上分享了完整的代碼,鏈接如下:https://gist.github.com/charlee/bc865ba8aac295dd997691310514e515正好100行 ,跑一下試試:

  1. Scraping /r/Python... 
  2.   - Retrieving https://imgs.xkcd.com/comics/python_environment.png 
  3.       x fail or not html 
  4.   - Retrieving https://thenextweb.com/dd/2017/04/24/universities-finally-realize-java-bad-introductory-programming-language/#.tnw_PLAz3rbJ 
  5.       => done, title = "Universities finally realize that Java is a bad introductory programming language" 
  6.   - Retrieving https://github.com/numpy/numpy/blob/master/doc/neps/dropping-python2.7-proposal.rst 
  7.       x fail or not html 
  8.   - Retrieving http://www.thedurkweb.com/sms-spoofing-with-python-for-good-and-evil/ 
  9.       => done, title = "SMS Spoofing with Python for Good and Evil" 
  10.   ... 

抓取的新聞文件:

 

如何用 100 行 Python 代碼實現(xiàn)新聞爬蟲?這樣可算成功?

最后需要做的是將這個腳本放在服務(wù)器上,設(shè)置好cronjob每天跑一次,然后將生成的文件發(fā)到我的信箱。我沒有花太多時間關(guān)注細節(jié),所以其實這個腳本還有很多值得改進的地方。有興趣的話你可以繼續(xù)添加更多的功能,如提取圖像等。

責(zé)任編輯:未麗燕 來源: 今日頭條
相關(guān)推薦

2020-07-20 09:20:48

代碼geventPython

2015-08-10 11:09:09

Python代碼Python

2020-08-21 13:40:17

Python代碼人體膚色

2018-02-08 16:45:22

前端JS粘貼板

2021-11-03 18:01:21

Python爬蟲微信群

2021-12-16 06:21:16

React組件前端

2020-02-28 15:33:12

代碼人工智能檢測

2024-09-23 10:00:00

代碼Python

2023-05-04 07:34:37

Rust代碼CPU

2023-11-27 07:10:06

日志中間件

2018-06-19 08:35:51

情感分析數(shù)據(jù)集代碼

2015-02-09 10:43:00

JavaScript

2018-05-17 10:05:24

運行iPadPython

2018-01-10 22:19:44

2020-05-09 10:38:31

Python透視表數(shù)據(jù)

2019-05-05 09:46:01

Python代碼神經(jīng)網(wǎng)絡(luò)

2010-03-03 09:30:40

Python實現(xiàn)網(wǎng)頁爬

2018-01-23 09:17:22

Python人臉識別

2022-04-09 09:11:33

Python

2022-03-26 22:28:06

加密通信Python
點贊
收藏

51CTO技術(shù)棧公眾號