python網路爬蟲--爬取淘寶聯盟

閻_鬆發表於2018-07-17

網際網路是由一個個站點和網路裝置組成的大網,我們通過瀏覽器訪問站點,站點把HTML、JS、CSS程式碼返回給瀏覽器,這些程式碼經過瀏覽器解析、渲染,將豐富多彩的網頁呈現我們眼前。

網路爬蟲,也叫網路蜘蛛(Web Spider),如果把網際網路比喻成一個蜘蛛網,Spider就是一隻在網上爬來爬去的蜘蛛。網路爬蟲就是根據網頁的地址來尋找網頁的,也就是全球統一資源定位符URL,用來定義網際網路上一個唯一的資源 例如:一張圖片、一個檔案、一段視訊都可以用url唯一確定。

爬蟲的基本流程:

  1. 傳送請求

  2. 獲得響應內容

  3. 解析內容

  4. 儲存資料

爬蟲所需的工具:

    1. 請求庫:requests,selenium(可以驅動瀏覽器解析渲染CSS和JS,但有效能劣勢(有用沒用的網頁都會載入))
    2.  解析庫:正則,xpath,beautifulsoup,pyquery
    3.  儲存庫:檔案,MySQL,Mongodb,Redis

下面實現一個爬蟲,爬取符合條件的淘寶聯盟網站的商品。

1. URL分析

     我們首先開啟淘寶聯盟網址,在搜尋欄隨便輸入一件 商品,比如“鞋子”

2. 按F12可以檢視訪問當前網頁傳送的所有請求 

現在發現沒東西,因為有的網站是動態載入的,當我們下拉滾動條時,看到有如下請求:

然後點選下一頁,有 下面的請求:

URL:http://pub.alimama.com/items/search.json?q=%E9%9E%8B%E5%AD%90&_t=1531837246570&toPage=2&perPageSize=50&auctionTag=&shopTag=yxjh&t=1531837861744&_tb_token_=58efe1f76686e&pvid=10_218.88.24.143_1018_1531837246340

通過分析這就是我們所要請求的URL。

3. 請求頭

User-agent:請求頭中如果沒有user-agent客戶端配置,服務端可能將你當做一個非法使用者host;

cookies:cookie用來儲存登入資訊

注意: 一般做爬蟲都會加上請求頭

請求頭需要注意的引數:

(1)Referrer:訪問源至哪裡來(一些大型網站,會通過Referrer 做防盜鏈策略;所有爬蟲也要注意模擬)

(2)User-Agent:訪問的瀏覽器(要加上否則會被當成爬蟲程式)

(3)cookie:請求頭注意攜帶

4、請求體

    如果是get方式,請求體沒有內容 (get請求的請求體放在 url後面引數中,直接能看到)
    如果是post方式,請求體是format data

    ps:
    1、登入視窗,檔案上傳等,資訊都會被附加到請求體內
    2、登入,輸入錯誤的使用者名稱密碼,然後提交,就可以看到post,正確登入後頁面通常會跳轉,無法捕捉到post

5、響應狀態碼

  200:代表成功

  301:代表跳轉

  404:檔案不存在

  403:無許可權訪問

  502:伺服器錯誤

2. 請求並獲取響應

我們通過不同的方式來分別請求並獲取響應,具體實現如下:

1.url請求,獲取響應的json資料,並將json資料轉換成dict 採用 urllib2 實現 

#url請求,獲取響應的json資料,並將json資料轉換成dict  採用 urllib2 實現
def getJSONText(url):
    try:
        page = urllib2.urlopen(url)
        data = page.read()
        #print (data)
        #print (type(data))
        #dict_data = json.loads(data)
        dict_data = demjson.decode(data)
        #print dict_data
        #print type(dict_data)
        return dict_data
    except:
        return ""

2.#url請求,獲取響應的json資料,並將json資料轉換成dict 採用 新增請求頭、設定代理的方式 實現

#url請求,獲取響應的json資料,並將json資料轉換成dict  採用 新增請求頭、設定代理的方式 實現
def getJSONText2(url):
    try:
        proxies = {
            "http": "http://221.10.159.234:1337",
            "https": "https://60.255.186.169:8888",
        }
        headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36'}
        data = requests.get(url, headers=headers,proxies=proxies).text
        print (data)
        print (type(data))
        # dict_data = json.loads(data)
        dict_data = demjson.decode(data)
        print dict_data
        print type(dict_data)
        return dict_data
    except:
        return "" 

3.採用selenium實現 

#url請求,獲取響應的json資料,並將json資料轉換成dict  採用selenium實現
def get_browser_text(url):
    #browser = webdriver.Chrome(executable_path="C:\\Program Files (x86)\\Google\\Chrome\\Application\\chromedriver.exe")
    browser = webdriver.Firefox(executable_path="C:\\Program Files (x86)\\Mozilla Firefox\\geckodriver.exe")
    try:
        browser.get(url)
        print(browser.page_source)
        browserdata = browser.page_source
        browser.close()
        # res = r'<a .*?>(.*?)</a>'
        res = r'<pre .*?>(.*?)</pre>'
        json_data = re.findall(res, browserdata, re.S | re.M)
        print json_data
        for value in json_data:
            print value

        dict_data = demjson.decode(json_data)
        print 'dict_data:'
        print dict_data
        # print type(dict_data)
        return dict_data
    except:
        return ""

4.

# 獲取單個商品的HTML程式碼並用正則匹配出描述、服務、物流3項引數  採用urllib2
def getHTMLText(url):
    try:
        data = urllib2.urlopen(url).read()
        res = r'<dd class="tb-rate-(.*?)"'
        data_list = re.findall(res, data, re.S | re.M)
        print type(data_list)
        print data_list[0]
        #for value in mm:
        #   print value
        return data_list
    except:
        return  ""

3. 爬蟲完整程式碼如下:

#coding=utf-8
__author__ = 'yansong'
# 2018.07.12
# 抓取淘寶聯盟 比率>10 ,描述、服務、物流3項引數高於或持平於同行業的商品圖片。


import json
import demjson
import urllib2
import os
import time
import requests
import re
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
import sys
reload(sys)
sys.setdefaultencoding('utf8')

path_name = u'T恤寬鬆日系男款'  #圖片儲存的資料夾名稱
Myname = u'T恤寬鬆日系男款'   #搜尋關鍵字

# 建立資料夾
path = os.getcwd()   				     # 獲取此指令碼所在目錄
new_path = os.path.join(path,path_name)
if not os.path.isdir(new_path):
	os.mkdir(new_path)

#url請求,獲取響應的json資料,並將json資料轉換成dict  採用selenium實現
def get_browser_text(url):
    #browser = webdriver.Chrome(executable_path="C:\\Program Files (x86)\\Google\\Chrome\\Application\\chromedriver.exe")
    browser = webdriver.Firefox(executable_path="C:\\Program Files (x86)\\Mozilla Firefox\\geckodriver.exe")
    try:
        browser.get(url)
        print(browser.page_source)
        browserdata = browser.page_source
        browser.close()
        # res = r'<a .*?>(.*?)</a>'
        res = r'<pre .*?>(.*?)</pre>'
        json_data = re.findall(res, browserdata, re.S | re.M)
        print json_data
        for value in json_data:
            print value

        dict_data = demjson.decode(json_data)
        print 'dict_data:'
        print dict_data
        # print type(dict_data)
        return dict_data
    except:
        return ""


#url請求,獲取響應的json資料,並將json資料轉換成dict  採用 urllib2 實現
def getJSONText(url):
    try:
        page = urllib2.urlopen(url)
        data = page.read()
        #print (data)
        #print (type(data))
        #dict_data = json.loads(data)
        dict_data = demjson.decode(data)
        #print dict_data
        #print type(dict_data)
        return dict_data
    except:
        return ""


# 獲取單個商品的HTML程式碼並用正則匹配出描述、服務、物流3項引數  採用urllib2
def getHTMLText(url):
    try:
        data = urllib2.urlopen(url).read()
        res = r'<dd class="tb-rate-(.*?)"'
        data_list = re.findall(res, data, re.S | re.M)
        print type(data_list)
        print data_list[0]
        #for value in mm:
        #   print value
        return data_list
    except:
        return  ""


#url請求,獲取響應的json資料,並將json資料轉換成dict  採用 新增請求頭、設定代理的方式 實現
def getJSONText2(url):
    try:
        proxies = {
            "http": "http://221.10.159.234:1337",
            "https": "https://60.255.186.169:8888",
        }
        headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36'}
        data = requests.get(url, headers=headers,proxies=proxies).text
        print (data)
        print (type(data))
        # dict_data = json.loads(data)
        dict_data = demjson.decode(data)
        print dict_data
        print type(dict_data)
        return dict_data
    except:
        return ""


def getJSONText3(url):
    try:
        driver = webdriver.Chrome(executable_path = "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chromedriver.exe")
        driver.get(url)  # 訪問淘寶寶貝頁面,獲取cookie
        # driver.get(taobao_comment_url)  # 直接訪問寶貝評論會被反爬蟲檢測到。上一步獲得cookie後可得到評論資料
        print(driver.find_element_by_xpath('/html/body').text)
        data = driver.find_element_by_xpath('/html/body').text
        #data = requests.get(url, headers=headers,proxies=proxies).text
        print (data)
        print (type(data))
        # dict_data = json.loads(data)
        dict_data = demjson.decode(data)
        print dict_data
        print type(dict_data)
        return dict_data
    except:
        return ""


def mytaobao_spider():
    # 每一頁的url  迴圈抓取60頁
    for page in range(0, 60):

        url = (
        'http://pub.alimama.com/items/search.json?q=%s&_t=1531556288035&toPage=%d&perPageSize=50&auctionTag=&shopTag=yxjh&t=1531556427336&_tb_token_=7e73deef30e18&pvid=10_117.136.70.61_599_1531556285246' % (
        Myname, page))
        # url = ('http://pub.alimama.com/items/channel/qqhd.json?q=%s&channel=qqhd&_t=1531121449018&toPage=%d&perPageSize=50&shopTag=&t=1531125125414&_tb_token_=eeee6ee3be688&pvid=19_118.112.188.32_688_1531125125232'%(Myname,page))
        # url = ('http://pub.alimama.com/items/search.json?q=%E9%9E%8B%E5%AD%90&_t=1531368912715&auctionTag=&perPageSize=50&shopTag=yxjh&t=1531368913289&_tb_token_=e370663ebef17&pvid=10_118.112.188.32_9532_1531368912863')
        print url
        time.sleep(2)  # 延時2秒,新增延時操作是因為淘寶有反爬蟲機制,過於頻繁的訪問IP可能會被限制
        url_data = getJSONText(url)
        # 一頁中每件商品的標籤資訊
        for i in range(0, 50):
            time.sleep(1)
            try:
                # print type(url_data['data']['pageList'][i]['pictUrl'])
                pictUrl = url_data['data']['pageList'][i]['pictUrl']  # 圖片url
                sellerId = url_data['data']['pageList'][i]['sellerId']  # 商品id
                auctionUrl = url_data['data']['pageList'][i]['auctionUrl']  # 淘寶連結
                auctionId = url_data['data']['pageList'][i][
                    'auctionId']  # 淘寶連結 =  'http://item.taobao.com/item.htm?id=%d'%(auctionId)
                tkRate = url_data['data']['pageList'][i]['tkRate']  # 比率
                zkPrice = url_data['data']['pageList'][i]['zkPrice']  # 價格

                # 需要抓取比率大於10.00的商品資訊
                if tkRate > 10.00:
                    # time.sleep(1)
                    # print '詳細資訊:'
                    # print type(tkRate)
                    # print type(zkPrice)
                    # print '比率:%f' % (tkRate)
                    # print '價格:%f' % (zkPrice)
                    # print sellerId
                    # print auctionId
                    # print pictUrl
                    # print auctionUrl  # 淘寶連結
                    # print type(sellerId)
                    print auctionUrl

                    # 每件商品的子url (描述相符、發貨速度、服務態度 等資訊)
                    # sub_url = ('http://pub.alimama.com/pubauc/searchPromotionInfo.json?oriMemberId=%d&blockId=&t=1531369204612&_tb_token_=e370663ebef17&pvid=10_118.112.188.32_760_1531368931581' % (sellerId))
                    sub_url = auctionUrl  # 每件商品的淘寶url
                    sub_url_data = getHTMLText(sub_url)  # 獲取店鋪的 描述、服務、物流 資訊
                    print type(sub_url_data)
                    print len(sub_url_data)

                    # 如果返回的是空字串, 則說明沒有取到我們想要的欄位,是因為淘寶有不同的頁面,對於這種頁面我們需要進一步分析下面的url
                    if (len(sub_url_data) == 0):
                        info_url = ('https://world.taobao.com/item/%d.htm' % (auctionId))
                        info_data = urllib2.urlopen(info_url).read()
                        res_info = r'<li class="([^s].*?)<img'
                        tmp_url_data = re.findall(res_info, info_data, re.S | re.M)
                        print "tmp_url_data:"
                        for value1 in tmp_url_data:
                            print value1

                        sub_url_data = []
                        score_list = [x[0:4] for x in tmp_url_data]  # 擷取前面5位
                        print 'new_list:'
                        for score in score_list:
                            print score
                            if score == 'down':
                                score = 'lower'  # d第一種頁面與第二種頁面返回的店鋪評定資訊不同,需轉換成統一的方便後面處理,將 down 轉換為 lower
                            sub_url_data.append(score)

                        print '替換後的list元素:'
                        for level_data in sub_url_data:
                            print level_data
                    # 如果3項評定引數都不是‘lower’ 就將圖片和相關資訊抓取出來   任意一項引數為‘lower’都不符合要求
                    if ((not (sub_url_data[0] == 'lower')) and (not (sub_url_data[1] == 'lower')) and (
                    not (sub_url_data[2] == 'lower'))):
                        # for value in sub_url_data:
                        #   print value

                        mypictUrl = 'http:' + pictUrl  # 圖片url
                        picture_content = urllib2.urlopen(mypictUrl).read()
                        picture_name = auctionUrl + '.jpg'  # 拼接圖片名稱
                        print picture_name
                        time.sleep(1)

                        # 需要寫入檔案的資訊
                        spider_info = 'url:' + url + '\n' + '  sub_url:' + sub_url + '\n' + '  淘寶連結:' + auctionUrl + '\n' + '  mypictUrl:' + mypictUrl + '\n\n'
                        try:
                            # 寫圖片
                            index_num = picture_name.index('id=')
                            with open(path_name + '/' + picture_name[index_num:], 'wb') as code:
                                code.write(picture_content)
                            # 寫URL資訊
                            with open(path_name + '/' + 'spider.txt', 'a') as spider_code:
                                spider_code.write(spider_info)
                        except (IOError, ZeroDivisionError), e:
                            print e
                            print "Error: 沒有找到圖片檔案或讀取檔案失敗"
                        else:
                            print "圖片寫入成功"

                        time.sleep(1)

            except (IndexError, KeyError, TypeError), e:
                print e
                print "每件商品資訊讀取失敗"
            else:
                pass
                # print "每件商品的標籤資訊讀取成功"


if __name__ == '__main__':
    mytaobao_spider()







4. 執行效果

抓取的圖片在指定的目錄下:

同時寫了一個spider.txt檔案,裡面是詳細的URL資訊:

單個商品的淘寶連結如:http://item.taobao.com/item.htm?id=566676904510

淘寶連結構成: http://item.taobao.com/item.htm?+id=566676904510    抓取的圖片名稱是以商品的id來命名的,我們根據圖片就可以快速找到該商品,該爬蟲抓取的都是  比率>10 ,描述、服務、物流3項引數高於或持平於同行業的商品,也可以根據自己需要,抓取價格、銷售量等符合自己要求的商品。

 

相關文章