在知道目標之後,接著就要決定如何達成。網路爬蟲的概念很簡單,就是進入網頁之後,將網站中的原始碼擷取下來,透過Xpath或是Html節點的方式來找到目標字串。根據上次[Python][教學] 網路爬蟲(crawler)實務(上)--網頁元件解析分析的內容,我們的爬網策略大致上會是:
- 進入搜尋頁面>找到店家網址>進入店家頁面>擷取資料
根據這樣的流程,將他拆解成更符合爬蟲程式的邏輯:
- 進入搜尋頁面
- 搜尋頁面有多個頁面,透過參數一次抓取n個搜尋頁面(搜尋網頁數n)
- 從搜尋頁面中解析出店家的網址,每頁有m個店家網址(店家網頁數m)
- 進入店家網址,解析出需要用的資訊(搜尋網頁數=n * m)
廢話不多說直接看code:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
##import 必要套件 | |
import requests | |
from bs4 import BeautifulSoup | |
import HTMLParser | |
import time | |
from random import randint | |
import sys | |
from IPython.display import clear_output | |
##從搜尋頁面擷取店家網址(因為搜尋頁面的電話是圖片不好抓) | |
links = ['http://www.ipeen.com.tw/search/all/000/1-100-0-0/?p=' + str(i+1) + 'adkw=東區&so=commno' for i in range(10)] | |
shop_links=[] | |
for link in links: | |
res = requests.get(link) | |
soup = BeautifulSoup(res.text.encode("utf-8")) | |
shop_table = soup.findAll('h3',{'class':'name'}) | |
##關在a tag裡的網址抓出來 | |
for shop_link in shop_table: | |
link = 'http://www.ipeen.com.tw' + [tag['href'] for tag in shop_link.findAll('a',{'href':True})][0] | |
shop_links.append(link) | |
##避免被擋掉,小睡一會兒 | |
time.sleep(1) | |
##建立變項檔案的header | |
title = "shop" + "," + "category" + "," + "tel" + "," + "addr" + "," + "cost" + "," + "rank" + "," + "counts" + "," + "share" + "," + "collect" | |
shop_list = open('shop_list.txt','w') | |
##先把header寫進去 | |
shop_list.write(title.encode('utf-8') + "\n") | |
for i in range(len(shop_links)): | |
res = requests.get(shop_links[i]) | |
soup = BeautifulSoup(res.text.encode("utf-8")) | |
header = soup.find('div',{'class':'info'}) | |
shop = header.h1.string.strip() | |
##做例外處理 | |
try: | |
category = header.find('p', {'class':'cate i'}).a.string | |
except Exception as e: | |
category = "" | |
try: | |
tel = header.find('p',{'class': 'tel i'}).a.string.replace("-","") | |
except Exception as e: | |
tel = "" | |
try: | |
addr = header.find('p', {'class': 'addr i'}).a.string.strip() | |
except Exception as e: | |
addr = "" | |
try: | |
cost = header.find('p', {'class':'cost i'}).string.split()[1] | |
except Exception as e: | |
cost = "" | |
try: | |
rank = header.find('span', {'itemprop': 'average'}).string | |
except Exception as e: | |
rank = "" | |
try: | |
counts = header.find_all('em')[0].string.replace(',','') | |
except Exception as e: | |
counts = "" | |
try: | |
share = header.find_all('em')[1].string.replace(',','') | |
except Exception as e: | |
share = "" | |
try: | |
collect = header.find_all('em')[2].string.replace(',','') | |
except Exception as e: | |
collect = "" | |
##串起來用逗號分格(應該有更好的方法,但是先將就用用) | |
result = shop + "," + category + "," + tel + "," + addr + "," + cost + "," + rank + "," + counts + "," + share + "," + collect | |
shop_list.write(result.encode('utf-8') + "\n") | |
##隨機睡一下 | |
time.sleep(randint(1,5)) | |
clear_output() | |
print i | |
sys.stdout.flush() | |
shop_list.close() | |
- 這次的爬網流程雖然簡單,但是還是有幾個要注意的地方:
- time.sleep: 這次總共抓了n * m 個網頁,短時間的大量抓取會消耗網站資源,影響網站運行,所以通常有品的爬網者會設定睡眠時間,避免造成對方主機的負擔
- try except: 當需要自動抓大量欄位時,一定要考慮或是注意到你要抓的欄位可能不是每個頁面都有提供,所以要加上例外處理才能避免錯誤而跳出程式
- 更為普遍的xpath設定: 如果只抓一兩個頁面,xpath要怎樣設都可以,也可以很簡單的利用數數的方式去取得標籤。但是如果要抓大量網頁,每個網頁的每個節點的數量可能會不一樣,最好多看幾個網頁原始碼,找到每個標籤在結構上的固定位置,避免抓錯欄位。
你好 我是teddy 請問你第13行的 str(i+1),是做什麼用的?
回覆刪除當作變數來翻頁嗎?
不是欸,那是一個List Comprehensive,一次產生一串網址用的
刪除請問你的程式是怎麼Run的,比如說要爬一個網站的載點網址 伊莉討論區-->電影下載區-->玩命關頭-->把網站載點爬下來 是這樣嗎?
回覆刪除還有可不可以把你程式附註解然後寄給我,因為才剛開始學PYTHON,不知道行不行,如果不行就不用麻煩了,謝謝。
或者貼到我的部落格裏面http://xeriom90862.blogspot.tw/
刪除您好,我的程式都已經貼上來了(汗)裡面也有註解(汗汗)
刪除我是用ipython來執行的
作者已經移除這則留言。
刪除想跟你詢問一下我有跑過你的程式但她總是跟我說你程式第13行錯誤,請問這有什麼問題嗎,是我沒安裝好模塊嗎
刪除作者已經移除這則留言。
回覆刪除方便留下錯誤訊息嗎? 我這邊程式沒有錯誤喔
回覆刪除以解決了,不好意思,另外我想跟您請教一下該如何用PYTHON下載種子載點到指定目錄下(目前卡在怎樣丟到指定目錄下)
回覆刪除請參考:
刪除import urllib2
mp3file = urllib2.urlopen("http://www.example.com/songs/mp3.mp3")
output = open('test.mp3','wb') #可用絕對路徑修改目標目錄
output.write(mp3file.read())
output.close()
作者已經移除這則留言。
刪除shop = header.h1.string.strip()
回覆刪除請問為何這行會有錯誤
ttributeError: 'NoneType' object has no attribute 'h1'
初學python問題比較淺 感謝Bryan 大大指教
又或者我想抓他下方的
刪除商家名稱
XXX shop name
都抓不出來
可能語法錯了都報
'NoneType' object has no attribute 'find_all'
可否開示,感恩您~~
被擋了
刪除div id="shop-details" 那邊
因為header有欄位沒抓東西,所以變成NoneType,可以檢查一下header抓到的內容是部分還是全部錯誤,部分錯誤的話可以設try except排除
刪除我也是在執行
刪除header = soup.find('div',{'class':'info'})
這行的時候他傳回的是一個空的list
想請問一下為甚麼會這樣還有該怎麼解決?
另外想問一下你的程式碼是用findAll()這個跟官方文件的find_all()是一樣的嗎?
因為網頁改版了 原本的位置抓不到了XD
刪除請大家自行看原始HTML修改囉(怕太容易造成網頁困擾)
很淺顯易懂的文章,對初入門的我幫助很大,看到資料整理出來的感覺很棒,謝謝囉!
刪除太棒了,恭喜你!
刪除作者已經移除這則留言。
回覆刪除你好 想跟你詢問第19行的程式碼 -> [tag['href'] 這是什麼意思?
回覆刪除ps 超愛你的程式碼 簡單好懂!
謝謝您的喜歡:)
刪除[tag['href'] for tag in shop_link.findAll('a',{'href':True})]
這是一個python的 comprehensive list, tag for tag in shop_link 表示從後面的shop_link.find取出各別元素並命名成tag,
tag['href'], 表示從每個tag裡面,取出['href']中的字串
謝謝版大!
回覆刪除