Python3 大型網路爬蟲實戰 004 — scrapy 大型靜態商城網站爬蟲專案編寫及資料寫入資料庫實戰 — 實戰:爬取淘寶
開發環境
- Python第三方庫:lxml、Twisted、pywin32、scrapy
- Python 版本:python-3.5.0-amd64
- PyCharm軟體版本:pycharm-professional-2016.1.4
- 電腦系統:Windows 10 64位
如果你還沒有搭建好開發環境,請到這篇部落格。
- 本文中的原始碼在github這裡:https://github.com/AoboJaing/thirdDemo/
本篇博文的重點內容:
- 有一些資料,在原始碼上找不到的,這個時候需要使用 — 抓包。
- Python呼叫MySQL資料庫
本爬蟲專案的目的是:某個關鍵字在淘寶上搜尋到的所有商品,獲取所有商品的: 商品名字、商品連結、商品價格、商品的評論。
開始實戰
建立一個爬蟲專案
scrapy startproject thirdDemo
設定防反爬機制(settings.py 檔案)
請參考這篇部落格:給 Scrapy 爬蟲專案設定為防反爬。
分析網站
- 分析網頁介面
- 分析網址結構
- 分析網頁原始碼
1 . 分析網頁介面:
我們在淘寶網的搜尋欄裡面搜尋關鍵字,比如“小吃”。它一共會輸出100頁。
可見:100頁的搜尋結果是淘寶的上限。(最多100頁)
2 . 分析網址結構:
當我們點選頁面進行瀏覽時,我們發現不同的頁面的網址有規律,並且下面是我們找到的規律:
- 紅色部分是一模一樣的。
- 刪除紅色部分,將剩下的組成網址,一樣可以正常的瀏覽原網頁。
q=
後面是“小吃”的編碼形式。s=
後面的數值等於44*(當前頁面-1)
開始寫爬蟲程式(taobao.py 檔案)
建立一個爬蟲檔案(taobao.py 檔案)
cd thirdDemo
scrapy genspider -t basic taobao taobao.com
使用PyCharm軟體開發,使用PyCharm軟體開啟 thirdDemo專案。
新增需要使用的儲存容器物件(items.py檔案)
先到 items.py
檔案裡面的ThirddemoItem()
函式裡面建立儲存用到容器(類的例項化物件)
class ThirddemoItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
title = scrapy.Field()
link = scrapy.Field()
price = scrapy.Field()
comment = scrapy.Field()
pass
得到搜尋關鍵字對應的所有搜尋頁面(taobao.py檔案)
在回撥函式parse()
中,建立一個變數(key
)來儲存關鍵詞(零食
)。然後在使用一個for
迴圈來爬取所有的網頁。然後使用scrapy.http
裡面的Request
來在parse()
函式返回(返回一個生成器(yield))一個網頁原始碼:
# -*- coding: utf-8 -*-
import scrapy
from scrapy.http import Request
class TaobaoSpider(scrapy.Spider):
name = "taobao"
allowed_domains = ["taobao.com"]
start_urls = ['http://taobao.com/']
def parse(self, response):
key = '小吃'
for i in range(0, 2):
url = 'https://s.taobao.com/search?q=' + str(key) + '&s=' + str(44*i)
print(url)
yield Request(url=url, callback=self.page)
pass
def page(self, response):
pass
(注意:我們上面通過觀察網頁已經知道了,搜尋得到的頁面有100頁,但是我們現在是測試階段,不爬這麼多頁,上面的程式碼我們只爬了2頁)
執行一下:
程式沒有問題。
得到所有商品的id
我們現在的目標是得到搜尋頁面中所有商品的連結。
現在,我們觀察搜尋頁面的原始碼,我們找這些商品連結的規律,能否通過什麼商品id
之類的資訊,然後通過商品id
來構造出商品的連結網址。
幸運的是:確實可以這麼做。
我發現,不管是搜尋的商品,不管是在淘寶裡、天貓裡、天貓超市裡,商品的連結網址都可以用下面的格式去構造:
https://item.taobao.com/item.htm?id=商品的id
所以,現在我們要做的是:先提取商品的id:(使用正規表示式)
對搜尋結果的網頁隨便一個地方右鍵:檢視網頁原始碼(V):
(我發現:通過在瀏覽器中按F12 和 右鍵來 檢視網頁原始碼 這兩種檢視原始碼得到的原始碼不一樣,後者得到的原始碼和爬蟲爬取的原始碼一致,而前者和爬蟲爬取的不一致。)
所有我們不能使用Xpath表示式來用過標籤獲取商品id了。只能使用正規表示式了。
我想可能的原因是:搜尋頁面可能是動態構造出來的,所以使用Xpath表示式是不能對這種網址的原始碼進行提取資訊的。(我瞎想的,不知道是否正確。不過事實其實是:使用Xpath表示式是提取不了有效資訊的。)
然後隨便點選進入一個商品的連結網頁,在這個網頁的網址裡面就可以找到這個商品的id:
然後在剛剛開啟的原始碼中,查詢這個id:
我們通過觀察發現,使用"nid":"
就可以找到這個搜尋結果頁面裡面所有商品的id:
這個頁面裡面一共是36個商品,沒錯。
Q: 你可能發現了,這個搜尋網頁裡面搜尋到結果是48個商品,我們得到的是36個,是不是少了?
A: 沒有少,這就是淘寶的營銷策略。一個搜尋頁面一共有48個商品,但是其中有10多個商品是重複的!其中甚至有個商品在這個搜尋頁面中重複出現了三次,不信,你可以仔細的找找。
所以,商品的id可以使用下面的正規表示式獲取:
'"nid":"(.*?)"'
我們在page()
方法中得到爬取到的網頁原始碼的 body
標籤裡面的所有資訊:
先宣告一點:
爬取到的網頁原始碼是:以網頁原始碼中指定的編碼方式編碼得到的bytes資訊。
我們需要得到對應的解碼資訊:
body = response.body.decode('utf-8')
response.body
它預設是二進位制格式,所以我們在使用它之前要給它解碼:decode('utf-8')
,為了避免出錯,我給它傳第二個引數:ignore
。
page()
函式中的程式碼現在是下面這個樣子的:
def page(self, response):
body = response.body.decode('utf-8','ignore')
pattam_id = '"nid":"(.*?)"'
all_id = re.compile(pattam_id).findall(body)
print(all_id)
pass
執行試試看:
得到所有商品的連結網址
現在得到了所有商品的ip,現在通過這些ip,構造出所有商品的網址。得到了連結後,就可以去爬這個網頁的原始碼了:(下面的程式碼中,在next()
方法中將商品的網頁網址列印了出來)
import re
def page(self, response):
body = response.body.decode('utf-8','ignore')
pattam_id = '"nid":"(.*?)"'
all_id = re.compile(pattam_id).findall(body)
# print(all_id)
for i in range(0, len(all_id)):
this_id = all_id[i]
url = 'https://item.taobao.com/item.htm?id=' + str(this_id)
yield Request(url=url, callback=self.next)
pass
pass
def next(self, response):
print(response.url)
pass
執行試試看:(自動的將網址調整到正確的網址上。比如天貓或者天貓超市之類的子域名)
獲取商品的具體資訊(taobao.py 檔案)
獲取商品的名字
現在在next()
回撥函式中例項化一個開始時在items.py
檔案裡面建立的專案的儲存容器物件。然後,我們就可以直接使用它了。
所以現在在 taobao.py
檔案的上面新增這個檔案:
from thirdDemo.items import ThirddemoItem
現在我們要得到商品的標題。
我們儘量從原始碼中的資訊提取,如果原始碼中沒有的資訊,我們在使用抓包的凡是提取。
標題是可以直接在原始碼中提取的:(觀察網頁原始碼,直接中Xpath
表示式)
天貓或者天貓超市的商品的標題可以使用下面的Xpath表示式提取:
title = response.xpath("//div[@class='tb-detail-hd']/h1/text()").extract()
淘寶的商品的標題可以使用下面的Xpath表示式提取:
title = response.xpath("//h3[@class='tb-main-title']/@data-title").extract()
所以,這裡提取標題,我們需要一個判斷語句,判斷這個商品的網址連結是天貓的還是淘寶的。
偽碼如下:
if 不是淘寶的網址:
title = response.xpath("//div[@class='tb-detail-hd']/h1/text()").extract() # 天貓或者天貓超市
else:
title = response.xpath("//h3[@class='tb-main-title']/@data-title").extract() # 淘寶
我們的判斷標準就是商品網址的子域名。子域名大致一共有三種:detail.tmall
(天貓)、chaoshi.detail.tmall
(天貓超市)、item.taobao
(淘寶)
def next(self, response):
# print(response.url)
url = response.url
pattam_url = 'https://(.*?).com'
subdomain = re.compile(pattam_url).findall(url)
# print(subdomain)
if subdomain[0] != 'item.taobao':
title = response.xpath("//div[@class='tb-detail-hd']/h1/text()").extract()
pass
else:
title = response.xpath("//h3[@class='tb-main-title']/@data-title").extract()
pass
self.num = self.num + 1;
print(title)
pass
執行試試看:
有的時候,偶爾會得到幾個
[]
,這是因為,你爬的太快的,淘寶的伺服器沒有同意你爬取這個商品的網頁。(所以提高防反爬機制,效果會好一些。)
獲取商品的連結網址(taobao.py 檔案)
(直接得到)
item['link'] = response.url
獲取商品的價格資訊(原價)(taobao.py 檔案)
正常的價格可以在商品網頁的原始碼裡面獲取,但是淘寶價(促銷價)在商品原始碼裡面沒有,這時就需要通過抓包來獲取。
淘寶:
天貓:
我們先獲取正常的價格。這裡也需要分淘寶和天貓,它們獲取正常價格的Xpath表示式或者正規表示式不同。
注意:這裡總結表示式,通過對商品頁面右鍵 -> 檢視網頁原始碼 的方式檢視原始碼。
if subdomain[0] != 'item.taobao':
pattam_price = '"defaultItemPrice":"(.*?)"'
price = re.compile(pattam_price).findall(response.body.decode('utf-8', 'ignore')) # 天貓
pass
else:
price = response.xpath("//em[@class = 'tb-rmb-num']/text()").extract() # 淘寶
pass
print(price)
提取商品的累計評論數量:(使用抓包的方式)(taobao.py 檔案)
淘寶:
天貓:
可以使用 : Fiddler4抓包軟體 或者 瀏覽器按F12->Network->Name->Response檢視抓包資訊
這裡,我通過瀏覽器進行抓包,找到了評論數所在的包:(一個一個的找)
淘寶:
觀察這個包的網址:
這個網址,我們可以在瀏覽器中複製,再訪問以下:(是可以正常訪問的)
https://rate.taobao.com/detailCommon.htm?auctionNumId=533237707421&userNumId=1990097437&ua=097UW5TcyMNYQwiAiwQRHhBfEF8QXtHcklnMWc%3D%7CUm5Ockt%2BQ3dDfkB8R35Eey0%3D%7CU2xMHDJ7G2AHYg8hAS8XKQcnCVU0Uj5ZJ11zJXM%3D%7CVGhXd1llXGlUYFRpV2tQaVFvWGVHekV8RHtBf0Z%2FQXRKdUx1T3VOYDY%3D%7CVWldfS0TMw8xBD8fIAAubQslcyU%3D%7CVmJCbEIU%7CV2lJGSQEORklGCMYOAI%2FADkZJREuEzMPMgc6GiYSLRAwDDEJNGI0%7CWGFcYUF8XGNDf0Z6WmRcZkZ8R2dZDw%3D%3D&callback=json_tbc_rate_summary
我發現上面的這個網址可以縮減為:
https://rate.taobao.com/detailCommon.htm?auctionNumId=533237707421
https://rate.taobao.com/detailCount.do?_ksTS=1480993623725_99&callback=jsonp100&itemId=533237707421
而這個
533237707421
就是商品的id。好了,找到這個網址的規律,現在可以> 手動構造這個評論數的網址了:天貓:
https://dsr-rate.tmall.com/list_dsr_info.htm?itemId=35338957824&spuId=235704813&sellerId=628189716&_ksTS=1480992656788_203&callback=jsonp204
可以縮減為:
https://dsr-rate.tmall.com/list_dsr_info.htm?itemId=35338957824
最後,我們發現:不管是淘寶還是天貓,都可以使用下面這個構造方式來得到含有正確評論數量的網址:
https://dsr-rate.tmall.com/list_dsr_info.htm?itemId=商品id
注意:使用
https://rate.taobao.com/detailCommon.htm?auctionNumId=商品id
這種網址也可以,但是在對天貓商品得到評價數量和網頁裡面顯示的不同。所以我們不使用這個構造方法。
所以通過商品id就可以得到含有評論數量資訊的包的網址。現在在next()
方法中需要通過商品的URL獲取商品的id。
我們從上面的圖中看到:天貓和淘寶的網址不同,所以,從網址中獲取商品id的正規表示式也就不同。下面的程式碼的功能就是從商品的url中提取商品id:
# 獲取商品的id(用於構造商品評論數量的抓包網址)
if subdomain[0] != 'item.taobao': # 如果不屬於淘寶子域名,執行if語句裡面的程式碼
pattam_id = 'id=(.*?)&'
this_id = re.compile(pattam_id).findall(url)[0]
pass
else:
# 這種情況是不能使用正規表示式的,正規表示式不能獲取字串最末端的字串
pattam_id = 'id=(.*?)$'
this_id = re.compile(pattam_id).findall(url)[0]
pass
print(this_id)
注意:
$
: 在正規表示式裡面的作用是:匹配字串末尾。舉例:當
url = 'https://item.taobao.com/item.htm?id=535023141744'
時,這是一個淘寶網站裡面的一個商品,現在我們想得到這個網址裡面的商品id。如果你把正規表示式寫成這個樣子:
pattam_id = 'id=(.*?)'
,是匹配不到結果的(商品id)。正規表示式是通過字串上下文來匹配你需要的資訊的,如果只有“上文”,沒有“下文”時,對於使用正規表示式匹配字串末端字串,需要在正規表示式中使用
$
。
執行試試看,一切都在掌控之中。
構造具有評論數量資訊的包的網址,並獲取商品的評論數量
得到目標抓包網址,獲取它的原始碼,然後提取評論數量:
import urllib
# 構造具有評論數量資訊的包的網址
comment_url = 'https://dsr-rate.tmall.com/list_dsr_info.htm?itemId=' + str(this_id)
# 這個獲取網址原始碼的程式碼永遠也不會出現錯誤,因為這個URL的問題,就算URL是錯誤的,也可以獲取到對應錯誤網址的原始碼。
# 所以不需要使用 try 和 except urllib.URLError as e 來包裝。
comment_data = urllib.request.urlopen(comment_url).read().decode('utf-8', 'ignore')
pattam_comment = '"rateTotal":(.*?),"'
comment = re.compile(pattam_comment).findall(comment_data)
# print(comment)
item['comment'] = comment
現在返回item
物件:
yield item
現在,我們就可以在pipline.py
檔案裡面來對我們得到的這些商品資料進行一些操作了,比如列印到終端或者儲存到資料庫中。
但在這之前,我們需要設定一下settings.py
檔案,將下面的程式碼的註釋去掉:
在pipline.py
檔案中對taobao.py
爬蟲檔案返回的item
物件進行處理(比如列印到終端,或者儲存到資料庫中)
將得到的資訊列印到終端中:
class ThirddemoPipeline(object):
def process_item(self, item, spider):
title = item['title'][0]
link = item['link']
price = item['price'][0]
comment = item['comment'][0]
print('商品名字', title)
print('商品連結', link)
print('商品正常價格', price)
print('商品評論數量', comment)
print('------------------------------\n')
return item
執行試試看:
下面是將得到的資訊儲存到資料庫中的操作:
第一件事情就是 啟動資料庫,啟動資料庫的程式碼一般我們是將它寫到預設的__init__(self)
函式中,這個方法就是最開始做的事情。
要想連線到資料庫,首先要有資料庫:使用MySQL資料庫
在python上要想使用MySqL資料庫,需要先安裝pymysql
庫這個模組。
有開啟資料庫的函式,就要有關閉資料庫的方法。
現在,我們在process()
函式中處理資料,將資料插入到資料庫裡面。並且加一個異常處理,因為我不希望程式執行的時候會出現錯誤而終止,並且我也不想
相關文章
- 大型商城網站爬蟲專案實戰網站爬蟲
- Python3 大型網路爬蟲實戰 003 — scrapy 大型靜態圖片網站爬蟲專案實戰 — 實戰:爬取 169美女圖片網 高清圖片Python爬蟲網站
- Python3 大型網路爬蟲實戰 — 給 scrapy 爬蟲專案設定為防反爬Python爬蟲
- 網路爬蟲——爬蟲實戰(一)爬蟲
- Python大型網路爬蟲專案開發實戰(全套)Python爬蟲
- 爬蟲實戰scrapy爬蟲
- Python3 大型網路爬蟲實戰 002 --- scrapy 爬蟲專案的建立及爬蟲的建立 --- 例項:爬取百度標題和CSDN部落格Python爬蟲
- python3網路爬蟲開發實戰_Python3 爬蟲實戰Python爬蟲
- Python靜態網頁爬蟲專案實戰Python網頁爬蟲
- 爬蟲實戰——58同城租房資料爬取爬蟲
- 大資料爬蟲專案實戰教程大資料爬蟲
- 網路爬蟲(六):實戰爬蟲
- 《Python3網路爬蟲開發實戰》教程||爬蟲教程Python爬蟲
- 網路爬蟲——專案實戰(爬取糗事百科所有文章)爬蟲
- Python網路爬蟲實戰小專案Python爬蟲
- Python網路爬蟲實戰專案大全!Python爬蟲
- Python網路爬蟲實戰Python爬蟲
- [Python3網路爬蟲開發實戰] 分散式爬蟲原理Python爬蟲分散式
- Python3 大型網路爬蟲實戰 001 --- 搭建開發環境Python爬蟲開發環境
- 爬蟲專案實戰(一)爬蟲
- 爬蟲實戰專案集合爬蟲
- 爬蟲實戰專案合集爬蟲
- Python爬蟲實戰:爬取淘寶的商品資訊Python爬蟲
- 爬蟲實戰爬蟲
- Java 爬蟲專案實戰之爬蟲簡介Java爬蟲
- Python3網路爬蟲開發實戰Python爬蟲
- Python【爬蟲實戰】提取資料Python爬蟲
- Python3網路爬蟲快速入門實戰解析Python爬蟲
- Python爬蟲 ---scrapy框架初探及實戰Python爬蟲框架
- Python網路爬蟲實戰專案大全 32個Python爬蟲專案demoPython爬蟲
- Python網路爬蟲實戰:爬取知乎話題下 18934 條回答資料Python爬蟲
- Python網路爬蟲資料採集實戰:Requests和Re庫Python爬蟲
- python3網路爬蟲開發實戰pdfPython爬蟲
- 【Python爬蟲9】Python網路爬蟲例項實戰Python爬蟲
- API商品資料介面呼叫爬蟲實戰API爬蟲
- python3網路爬蟲開發實戰_Python 3開發網路爬蟲(一)Python爬蟲
- 利用python編寫爬蟲爬取淘寶奶粉部分資料.1Python爬蟲
- 網路爬蟲——Urllib模組實戰專案(含程式碼)爬取你的第一個網站爬蟲網站