2017年6月3日 星期六

[Crawler] PTT Movie版 爬蟲擷取留言簡易範例

本範例用來了解爬蟲的簡易實作和相關知識。

在這之前呢,希望大家可以把HTML語法再複習一次,

如果還不會HTML的語法的話,建議可以到以下網站去練習一下,

https://www.codecademy.com/learn/all

只要練習有關HTML的課程就好,如果對CSS有興趣的話往後可以再深究,

假如還不太熟悉Python語法的話也可以到上面的網站進行練習唷!

Step 1. 安裝相關套件

假如在Ubuntu上使用的話,要先用pip安裝這三個套件requestsbeautifulsoup、lxml

pip install requests
pip install beautifulsoup4
pip install lxml

先解說一下這三個的功用:

1. requests 是用來發送HTTP請求及回應

2. BeautifulSoup 是用來分析與抓取HTML中的元素

3. lxml 讓解析HTML可以變得更快

若在Windows上的話可以安裝 Anaconda

使用Anaconda中的Jupyter notebook可以不用特別安裝上面那兩樣東西。

Step 2. 瀏覽要擷取的網頁

我們隨便選了一篇在電影版上的文章(看你想擷取哪一篇),

我選了一篇爆的文章,因為這樣留言數才會多,之後想分析的話會比較好分析XD

要擷取的網址大概會長這樣:

https://wwwtt.cc/bbs/movie/M.1496375334.A.C6B.html

灰色的部分呢是PTT電影版的文章列表(記得要把紫色部分改成index),

紫色的部分是每篇文章特有的index

Step 3. 開始撰寫程式

我們先import剛剛安裝的那兩個套件,而import有點像是C語言中的include,

而這邊比較特殊的是BeautifulSoup,它要先from bs4在import,

import requests
from bs4 import BeautifulSoup

在設定一個變數 url 來存放要擷取的網站,

之後我們會使用 requests.get() 這個函式,仿造HTTP的GET來瀏覽網站,

其目的主要是要取得此網站的原始碼

我們取得之後可以將它印出,

#所要擷取的網站網址
url = '自行選取想要的電影版網址'
#建立回應
response = requests.get(url)
#印出
print(response.text)

印出的內容大概會長這樣:

<!DOCTYPE html>
<html>
 <head>
  <meta charset="utf-8">
  

<meta name="viewport" content="width=device-width, initial-scale=1">

<title> 其文章標題 - 看板 movie - 批踢踢實業坊</title>
<meta name="robots" content="all">
<meta name="keywords" content="Ptt BBS 批踢踢">
<meta name="description" content="內文">

-----------------------------------(以下省略)------------------------------

取得原始碼後我們要將它做整理!

接著我們可以透過 (F12 開發者工具)來觀察網站的留言到底會放在哪個地方,




我們會發現其留言會放在 class = "push" 的 div 標籤中,

因為HTML的語法屬於階層式,它會把東西放在一層又一層的標籤裡,

因此我們需要一層一層的去了解其資料是放在哪一層,

這裡我們會使用到find_all()的方法來操作BeautifulSoup來找特定目標(也就是push)

#將原始碼做整理
soup = BeautifulSoup(response.text, 'lxml')
#使用find_all()找尋特定目標
articles = soup.find_all('div', 'push')
#印出
print(articles)

其印出的內容為 class = "push" 的 div 標籤中所有的東西,

包括推、噓、ID、留言、推文時間等,印出的結果如下:

[<div class="push"><span class="hl push-tag">推 </span><span class="f3 hl push-userid">yang560831</span><span class="f3 push-content">: 都不用爭執 10分鐘後我在這帖一次爆光光</span><span class="push-ipdatetime"> 06/02 11:50
-----------------------------------(以下省略)------------------------------

因為我們是要擷取留言,而留言它是放在 class = "f3 push-content"span 標籤裡,

只要把它從這邊撈出來基本上就大功告成了!

#取得留言內容
for article in articles:
    messages = article.find('span', 'f3 push-content').getText()
    print(messages)

印出結果如下:



撈出來後會發現前面會多個冒號

我們可以用replace()這個方法來去除掉冒號,

但去除掉之後還是會有空格怎麼辦?

這時候我們可以使用strip()來去除左右的空白格,

這樣就算大功告成了!

之後我們可以把它寫進txt或pickle中,

這樣就不用再每次去爬它了,也不用擔心資料會流失,

其完整程式碼如下:

import requests
from bs4 import BeautifulSoup

#所要擷取的網站網址
url = '自行選取想要的電影版網址'
#建立回應
response = requests.get(url)
#印出網站原始碼
#print(response.text)

#將原始碼做整理
soup = BeautifulSoup(response.text, 'lxml')
#使用find_all()找尋特定目標
articles = soup.find_all('div', 'push')

#寫入檔案中
with open('movie_message.txt','w') as f:
    for article in articles:
        #去除掉冒號和左右的空白
messages = article.find('span','f3 push-content').getText().replace(':','').strip()
        print(messages)
  f.write(messages + "\n")


Reference:

[1] https://github.com/leVirve/CrawlerTutorial

[2] Beautiful Soup Documentation,
https://www.crummy.com/software/BeautifulSoup/bs4/doc/

[3] Requests,http://docs.python-requests.org/en/master/

[4] PTT 電影版, https://www.ptt.cc/bbs/movie/index.html

[5] Python Cookbook 3rd Edition Documentation,
http://python3-cookbook.readthedocs.io/zh_CN/latest/index.html

如有疑問歡迎在下方留言告訴我唷~~~

8 則留言:

  1. 請問如果只要內文的部分要如何抓

    回覆刪除
    回覆
    1. import requests
      from bs4 import BeautifulSoup

      #所要擷取的網站網址
      url = 'https://www.ptt.cc/bbs/movie/M.1496375334.A.C6B.html'
      #建立回應
      response = requests.get(url)

      def checkformat(soup, class_tag, data, index, url):
      content = soup.select(class_tag)[index].text
      return content

      date = checkformat(soup, '.article-meta-value', 'date', 3, url)

      #將原始碼做整理
      soup = BeautifulSoup(response.text, 'lxml')
      #content 文章內文
      content = soup.find(id="main-content").text
      target_content = u'※ 發信站: 批踢踢實業坊(ptt.cc),'
      #去除掉 target_content
      content = content.split(target_content)
      #print(content)
      content = content[0].split(date)
      #print(content)
      #去除掉文末 --
      main_content = content[1].replace('--', '')
      #印出內文
      print(main_content)

      刪除
    2. 上面checkformat的部分要記得縮排一下
      留言縮排好像會被吃掉...

      刪除
  2. 同上, 也想請教大大在html中沒有被tag包住的內文跟作者修文置於留言間的回應要怎麼抓比較方便呢QQ?

    回覆刪除
    回覆
    1. 這部分我沒試過耶 哈哈
      我有空再研究好了 你先試試看目前我改的程式碼
      可以抓取內文了

      刪除
    2. 我自己有試出一個方法,是用bs4的type類型去篩,因為如果是頁面上單純的白字的話,type會跟有tag的不一樣,所以我用if str(type(...)) ...去篩出白字內容。但缺點是如果內文有上色之類的話在html中就會有tag,所以這方法只適用抓取未經任何處理的白字部分XD 目前在抓內文上也是參考python板用切的,應該跟你差不多ww

      刪除
  3. 你好, 想請問抓完留言怎麼把同帳號的留言合併 ? 謝謝

    回覆刪除

[Crawler] PTT Movie版 爬蟲擷取留言簡易範例

本範例用來了解爬蟲的簡易實作和相關知識。 在這之前呢,希望大家可以把 HTML 語法再複習一次, 如果還不會HTML的語法的話,建議可以到以下網站去練習一下, https://www.codecademy.com/learn/all 只要練習有關 HTML 的課程...