1. 程式人生 > >Python之scrapy使用教程

Python之scrapy使用教程

scrapy抓取西刺代理並用Excel儲存

(一)解析網站結構
①,首先瀏覽器訪問西刺代理首頁:西刺免費代理IP

②,對檢視網頁原始碼,對網頁結構有初步的瞭解

③,因為我們使用的是 scrapy 來抓取內容。所以,我們可以先使用 scrapy shell 來模擬下scrapy的訪問。cmd 中輸入 >>> scrapy shell "http://www.xicidaili.com/"
我們發現執行之後返回的states狀態碼是 503,出現了伺服器內部問題。其實不然,是因為伺服器將我們識別後拒絕了。解決方法:新增 USER_AGENT 資訊。
>>> scrapy shell -s USER_AGENT="瀏覽器的User-Agent資訊" "http://www.xicidaili.com/"

⑤,完成 ③ 步驟之後,便可以對該網站進行相關的除錯了

(二)程式碼編寫
首先根據網站結構確定我們的需求:
“IP地址”, “埠”, “伺服器地址”, “匿名”, “型別”, “速度”, “連線時間”, “存活時間”, “驗證時間”
然後正真進入程式碼編寫:

①,編寫 items

class IpItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    ip_address = scrapy.Field()
    port = scrapy.Field
() server_address = scrapy.Field() hiding = scrapy.Field() kind = scrapy.Field() speed = scrapy.Field() connect_time = scrapy.Field() alive_time = scrapy.Field()
②,編寫爬蟲spider
這裡對spider中間一些資訊的處理可能比較難理解,如果沒有了解到 openpyxl 表格操作的話,不過我這裡做了大量的註釋,相信還可以讓大家瞭解到一些關於 openpyxl 的一些簡單操作。
需要注意的是,因為每次返回的是網頁的一行tr
內容,所以在處理時 記得進行內容的拷貝。不然每次都會別後面一次內容覆蓋掉。
# -*- coding: utf-8 -*-
import scrapy
from IP.items import IpItem


class ProxySpider(scrapy.Spider):
    name = 'Proxy'
    allowed_domains = ['xici.com']
    start_urls = ['http://xici.com/']

    def start_requests(self):
        for page in range(1,11):
            yield scrapy.Request("http://www.xicidaili.com/nn/%d"%page)
        # 每次生成一個Request, 返回一頁的內容

    # 函式返回:一頁的內容。但是每次pipeline處理其中的一行
    def parse(self, response):
        item = IpItem()
        items = {}
        table_ip_list = response.xpath('//table[@id="ip_list"]')[0]     # 獲取第一個table的 selector 選擇器
        trs = table_ip_list.xpath('//tr')       #獲取每一行          選擇器物件可以使用 xpath 操作 再得到選擇器物件
        # 第一行資訊標題資訊不用採集
        for tr in trs[2:]:
            row = trs.index(tr)     # 增加索引,方便表格操作

            item["ip_address"] = tr.xpath('td[2]/text()').extract()[0]
            item['port'] = int(tr.xpath('td[3]/text()').extract()[0])   # port 設為數字
            if len(tr.xpath('td[4]/a/text()').extract()) is 0:
                item['server_address'] = '未知'       # 如果該欄為空,則填未知
            else:
                item['server_address'] = tr.xpath('td[4]/a/text()').extract()[0]
            item['hiding'] = tr.xpath('td[5]/text()').extract()[0]
            item['kind'] = tr.xpath('td[6]/text()').extract()[0]
            item['speed'] = tr.xpath('td[7]/div/@title').extract()[0]
            item['connect_time'] = tr.xpath('td[8]/div/@title').extract()[0]
            item['alive_time'] = tr.xpath('td[9]/text()').extract()[0]
            item['check_time'] = tr.xpath('td[10]/text()').extract()[0]

            item_copy = item.copy()            # 這裡一定要對item進行拷貝,光是賦值的話會被覆蓋
            items[row] = item_copy       # 通過建立字典方便在pipeline中來確定行數   
                                         # 如:{row:{"key":"value"}}形式  row為在表格中需要儲存的行數
                                        #  每次以一行資料為單位被處理
        return items
②,編寫pipeline處理item並儲存到.xlsx檔案
在編寫這個pipeline時,我反覆的除錯,發現一些傳進來的資訊格式是這樣的。比如我們在spider每次處理item 的格式為:{row:{“key”:”value”}} 形式,然後把他一次更新到自己定義的items 中,最後執行return語句。安裝常理來說,傳遞給pipeline也應該是包含跟多個item 字典型別的字典,那麼我們在pipeline處理的時候也應該通過迭代獲取每一個單獨的item
然而,事實並非如此。而是每次傳進pipeline的都是單獨的一個item 所以我們在pipeline中處理item引數是不能使用迭代。而是直接對 item 引數進行相關操作。
我想著應該是 scrapy 本身機制問題。你每往Item這個類進行一次資料傳遞的時候,都會被記憶,然後再傳遞給pipeline是會自動幫你迭代處理好資料。
這只是我的個人理解。畢竟官方文件中,只是說spider會把返回的item傳遞給pipelineRequest 傳遞給排程器。
# -*- coding: utf-8 -*-

# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: http://doc.scrapy.org/en/latest/topics/item-pipeline.html

from openpyxl import Workbook, load_workbook
from openpyxl.styles import colors, Alignment, Font
import os.path


# 每獲得一頁內容,建立一張新的sheet表格
# 第一次建立時,選擇第一個 sheet
class IpPipeline(object):
    def process_item(self, item, spider):
        # 例項化一個workbook物件
        if os.path.exists("Proxy.xlsx"):
            wb = load_workbook("Proxy.xlsx")
            ws1 = wb.create_sheet()  # 建立一張表
        else:
            wb = Workbook()
            ws1 = wb.active

        # 每次儲存一行資料
        for row in item:
            # 儲存第一列的 IP
            ws1.cell(row=row, column=1, value=item[row]["ip_address"])
            # 儲存第二列的埠 port
            ws1.cell(row=row, column=2, value = item[row]["port"])
            # 儲存第三列的伺服器地址 server_address
            ws1.cell(row=row, column=3, value = item[row]["server_address"])
            # 儲存第四列的匿名情況 hiding
            ws1.cell(row=row, column=4, value = item[row]["hiding"])
            # 儲存第五列的協議型別 kind
            ws1.cell(row=row, column=5, value = item[row]["kind"])
            # 儲存第六列的網路速度 speed
            ws1.cell(row=row, column=6, value = item[row]["speed"])
            # 儲存第七列的連線速度 connect_time
            ws1.cell(row=row, column=7, value = item[row]["connect_time"])
            # 儲存第八列的存活時間 alive_time
            ws1.cell(row=row, column=8, value = item[row]["alive_time"])
            # 儲存第九列的驗證時間 check_time
            ws1.cell(row=row, column=9, value = item[row]["check_time"])

        # 對工作簿樣式進行處理
        self.process_excel(work_book=wb, work_sheet=ws1)
        # 儲存工作簿
        wb.save('Proxy.xlsx')
        return item


    # 更改表格樣式
    # 傳入引數 工作簿物件wb, 表格物件ws1
    def process_excel(self, work_book, work_sheet):
        # 新增第一行
        first_row = ("IP地址", "埠", "伺服器地址", "匿名", "型別", "速度", "連線時間", "存活時間", "驗證時間")
        for col in range(1, len(first_row)+1):
            work_sheet.cell(row=1, column=col, value=first_row[col - 1])
        work_sheet.title = "代理頁"
        row_length = work_sheet.max_row  # 獲得包含資料的總行數int sheet.rows 返回所有行(含資料)可用來迭代
        col_length = work_sheet.max_column  # 獲取總列數

        work_sheet.sheet_properties.tabColor = "1072BA"   # sheet背景顏色
        # 設定列寬
        work_sheet.column_dimensions['A'].width = 22
        work_sheet.column_dimensions['C'].width = 20
        work_sheet.column_dimensions['F'].width = 16
        work_sheet.column_dimensions['G'].width = 16
        work_sheet.column_dimensions['H'].width = 16
        work_sheet.column_dimensions['I'].width = 20
        # 設定行高
        for row in range(0, row_length):
            work_sheet.row_dimensions[row+2].height = 20

        # 設定第一行的單元格樣式  居中,字型,顏色
        # 按列迭代
        for col in work_sheet.iter_cols(min_row=1, max_row=1, min_col=1,max_col=col_length):
            for cell in col:
                cell.alignment = Alignment(horizontal="center", vertical="center",)
                cell.font = Font(size=16, color="CD2626", bold=True, italic=False)      # 加粗 傾斜

        # 設定其他子元素格式
        # 按行迭代
        for row in work_sheet.iter_rows(min_col=1, min_row=2, max_col=col_length, max_row=row_length):
            # row 為一行的tuple物件
            for cell in row:
                cell.alignment = Alignment(horizontal="center", vertical="center")
                cell.font = Font(size=12, color="CD6090", bold=True, italic=False)

        work_book.save('Proxy.xlsx')



if __name__=="__main__":    # 用來此時本檔案,避免每次都要執行整個兒scrapy
    a = IpPipeline()
    a.process_item(item={},spider=None) # 主要用來測試相關表格操作是否合適
③,中介軟體MIDDLEWARES

middlerwares 的編寫其實也簡單。分為兩類,downloadermiddleware & spidermiddleware 。而我們常用的主要是是前者。下載器中介軟體。我們可以通過它來設定代理IP池和User-Agent池來防止爬蟲被網站BAN
每個中介軟體都需要繼承相應的類,這些類都包含以下一個或多個方法:
①:process_request(request, spider) 方法
②:process_response(request, response, spider) 方法
③:process_exception(request, exception, spider) 方法
分別用來處理請求的request,返回的response,以及跳出的異常。

具體步驟:
①,在自己工程的根目錄新建一個 mymiddlewares.py 檔案,我們在這裡面編寫middleware內容。
②,編寫代理IP中介軟體:

# coding : utf-8

from scrapy.downloadermiddlewares.httpproxy import HttpProxyMiddleware
import random

pool = [
    {"ip":"121.31.156.251:8123"},
    {"ip":"106.57.6.218:4326"},
    {"ip":"61.135.217.7:80"},
]

# 使用者代理池的中介軟體
class IPPOOLS(HttpProxyMiddleware):

    def process_request(self, request, spider):
        ip = random.choice(pool)
        #print("當前使用的IP是:%s" % ('http://'+ip["ip"]))
        # 將IP新增到request
        request.meta["proxy"] = 'http://'+ip["ip"]
        return None

: ②,編寫User-Agent中介軟體:

from scrapy.downloadermiddlewares.useragent import UserAgentMiddleware
from user_agent.base import generate_user_agent
# 使用者header池的中介軟體
class AGENTPOOLS(UserAgentMiddleware):
    '''
    def __init__(self, user_agent=''):
        self.user_agent = user_agent
    '''
    def process_request(self, request, spider):
        user_agent = generate_user_agent()
        print("這一次使用的是 %s" % user_agent)
        request.headers.setdefault(b'User-Agent', user_agent)

他們都繼承了自己的類,然後我們通過重寫裡面的方法就能實現制定功能。

(三)啟用設定
寫了這麼多,接下來當然是啟用我們改寫的東西。進入settings.py,新增自己相應的pipeline,middleware,.etc。然後就可以了。
關於這一塊我覺得挺簡單的。只需要把相應的註釋符 # 去掉就可以了。
(四)執行效果圖
爬取的前幾頁代理IP

有朋友可能說為什麼不採用直接儲存到.csv 檔案,這裡純屬個人愛好。方便處理一點這個。

好了,就到這了希望可以幫到需要的朋友。^_^