iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 9
0
AI & Data

Scrapy爬蟲與資料處理30天筆記系列 第 9

[Day 09] 實戰:用Requests&bs4 爬PTT (1)

  • 分享至 

  • xImage
  •  

嗨大家,今天我們要來說明如何爬ptt的文章啦!只需要用requests&beautifulSoup就行啦!
(關於此內容會拆分成兩天說明哦!)

在開始爬取一個網站之前一定要觀察該網頁的變化:

這次我們以爬美食板為例(下圖):

  • 可以發現ptt的url都會有規律:
    https://www.ptt.cc/bbs/<看板名稱>/index.html
    (也就是說今天你可以爬自己有興趣的版)

Imgur

  • 抓取該頁面所有文章連結:對一個文章右鍵>檢查,可以看到該文章標題與href,而href就是我們需要的內容。如下圖可以看出它在<a>標籤內且被<div class="title"></div>包覆。

Imgur

  • 為什麼需要找href呢?點進去可以發現href就是文章的連結(如圖):

Imgur

好了之後就開始寫程式碼吧!

  • 建立一個list變數article_href
  • 發送請求
  • 透過soup解析response回傳的文字(r.text)
  • 因為<a>標籤在<div class="title"></div>,用select取得所有divclass="title"的物件
import requests
from bs4 import BeautifulSoup
article_href = []
r = requests.get("https://www.ptt.cc/bbs/Food/index.html")
soup = BeautifulSoup(r.text,"html.parser")
results = soup.select("div.title")
print(results)

應該會得到以下結果:是一個list,裡面把該頁的div class="title"元素都取出來且裡面包覆著<a>標籤。

[<div class="title">
<a href="/bbs/Food/M.1540099186.A.315.html">[食記] 高雄岡山羊肉 - 舊市羊肉</a>
</div>, <div class="title">
<a href="/bbs/Food/M.1540104279.A.FDB.html">[食記] 蘆洲 Tiffany綠  98 nine eight land</a>
</div>, <div class="title">
(略)
<div class="title">
<a href="/bbs/Food/M.1535474674.A.9D5.html">Fw: [公告] 報考小天使資格及注意事項</a>
</div>]

我們知道results為一個list

for item in results:
    item_href = item.select_one("a").get("href")
    article_href.append(item_href)
print(article_href)

# ['/bbs/Food/M.1540099186.A.315.html', 
# '/bbs/Food/M.1540104279.A.FDB.html', 
# '/bbs/Food/M.1540106895.A.744.html', 
# '/bbs/Food/M.1540107608.A.D43.html', 
# '/bbs/Food/M.1540109077.A.62C.html',  
# '/bbs/Food/M.1540110023.A.39A.html', 
# '/bbs/Food/M.1355673582.A.5F7.html', 
# '/bbs/Food/M.1190944426.A.E6C.html', 
# '/bbs/Food/M.1128132666.A.0FD.html', 
# '/bbs/Food/M.1496532469.A.C36.html', 
# '/bbs/Food/M.1535474674.A.9D5.html']

這樣就取到該頁的所有連結了,不過這樣的話只有取到第一頁的文章,所以現在我們來看看如何切頁吧!
右上角有一個<上頁的按鈕,對它點擊右鍵>檢查

Imgur

一樣可以看到href,這個就是我們要切換頁面的連結,接下來要做的就是抓到它!

  • 取得divclassbtn-group下的a標籤
  • 回傳的結果可以看到要的「上 」在第3個Index
btn = soup.select('div.btn-group > a')
up_page_href = btn[3]['href']
next_page_url = 'https://www.ptt.cc' + up_page_href
print(next_page_url)
# https://www.ptt.cc/bbs/Food/index6958.html

我們可以用for迴圈定義要抓幾頁:

url="https://www.ptt.cc/bbs/Food/index.html"
for page in range(1,4):
    r = requests.get(url)
    soup = BeautifulSoup(r.text,"html.parser")
    btn = soup.select('div.btn-group > a')
    up_page_href = btn[3]['href']
    next_page_url = 'https://www.ptt.cc' + up_page_href
    url = next_page_url
    print(url)

最後整理一下,寫成這樣(本篇的完整程式碼):

import requests
from bs4 import BeautifulSoup
url="https://www.ptt.cc/bbs/Food/index.html"

def get_all_href(url):
    r = requests.get(url)
    soup = BeautifulSoup(r.text, "html.parser")
    results = soup.select("div.title")
    for item in results:
        a_item = item.select_one("a")
        title = item.text
        if a_item:
            print(title, 'https://www.ptt.cc'+ a_item.get('href'))
        
for page in range(1,4):
    r = requests.get(url)
    soup = BeautifulSoup(r.text,"html.parser")
    btn = soup.select('div.btn-group > a')
    up_page_href = btn[3]['href']
    next_page_url = 'https://www.ptt.cc' + up_page_href
    url = next_page_url
    get_all_href(url = url)

比較不同的是:

  • 定義了get_all_href()function,用來抓取每一頁的所有文章連結
  • if a_item:是因為可能會有文章刪除或不存在的可能,會得到None,所以需要先確認a_item是有值的,我們才取href

Imgur

如上圖,就可以看到我們抓了三頁的文章標題以及它的連結,是不是很簡單呢?那今天就到這裡了,明天將繼續說明如何再往裡面抓取文章的內容等資訊以及如何抓取圖片哦!

如果有問題歡迎留言告訴我,最近真的忙到昏了很擔心文章會有誤(塊陶~)


上一篇
[Day 08] Beautiful Soup 解析HTML元素
下一篇
[Day 10] 實戰:用Requests&bs4 爬PTT (2)
系列文
Scrapy爬蟲與資料處理30天筆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

2 則留言

1
renewang
iT邦新手 5 級 ‧ 2018-12-18 14:25:44

不好意思,又來請教了~請問一下,在這篇中用 'div.btn-group > a' 來選取上頁的網址,那和上一篇中使用空格的方式來選取如 'div.btn-group a' 有什麼不一樣呢?是否能舉個實例說明這兩個語法上的不同之處?謝謝!

plusone iT邦新手 5 級 ‧ 2018-12-19 13:58:58 檢舉

>的話,是只要下一層的哦!

renewang iT邦新手 5 級 ‧ 2018-12-20 01:46:32 檢舉

噢~我懂了~謝謝~

請問這是用程式撰寫的

0
Rex108
iT邦新手 5 級 ‧ 2019-11-24 14:29:52

請問在後面跑迴圈的時候原先的 URL不是已經被next_page_url改寫,那在最後call get_all_href(url = url),怎麼不是先爬上頁的內容呢

更新一下,自己跑一次後發現這次跑的程式碼,好像真的會少主頁的內容,直接從上頁開始跑

我要留言

立即登入留言