1. 程式人生 > >馭蟲術-Python(不定時更新)

馭蟲術-Python(不定時更新)


title: Python馭蟲術
tags: Python,爬蟲,小書匠
grammar_cjkRuby: true
---
配圖正在路上。

概念、工具 以及 協議

1.爬蟲

  • 爬蟲就是 模擬瀏覽器傳送網路請求,獲取響應,按照規則提取資料的程式
  • 爬蟲要的就是儲存資料,只抓取不儲存毫無用處

    2.資料流向

  • 呈現出來,用於網頁服務,或應用服務
  • 進行儲存、資料分析

3.需要的軟體與環境

  • 至少要有python環境
  • Pycharm或者其他IDE
  • chrome瀏覽:分析網頁原始碼

4.瀏覽器的請求

  • url:可以進行解碼或轉碼
  • 谷歌(瀏覽器)中的請求內容>Network
  • 爬蟲和瀏覽器請求的URL地址有一定差別。elements與Network中的原始碼會不一樣。

5.HTTP與HTTPS

  • Http:超文字傳輸協議:以明文方式進行傳輸,效率更高
  • Https:Http+SSL(安全套接字層):傳輸資料之前,將資料加密,之後解密,效率較低,但是更安全
  • HTTP協議:
    • 請求:
    1. 請求行
    2. 請求頭:
      • Status code:狀態碼
      • Conection: keep-alive 支援長連線
      • Cache-Control:max-age=0 快取控制為零
      • User-Agent
        :瀏覽器(核心)身份標識
      • Upgrade-Insecure-Requests:支援將不安全請求轉換的功能
      • Accept:表示我想接收什麼資料,q=權重
      • Cookie:瀏覽器儲存在使用者本地的一些資訊,包括使用者密碼,賬號資訊,網站校驗資訊(例如csrf跨站保護)。會被攜帶傳送給伺服器。Cookie可以被伺服器用來判斷客戶端是否是爬蟲。
      • Query-String-Parameters:請求資料(GET)
      • From-data:請求資料(POST)
      • 可以在Chorme中模擬多種版本的機器
    3. 請求體
      • GET請求沒有請求體,資料放在URL中
      • POST請求有請求體:攜帶資料,引數放置於請求體中,常用於登陸註冊、傳輸大文字。
    • 響應:
    1. 響應頭
      • set-Cookie:伺服器給本地設定的cookie資訊,伺服器通過該欄位設定cookie
    2. 響應體
      • URL地址對應的響應
    • 抓包:就是按一下F12,看一下瀏覽器傳送了什麼請求的資訊,這個過程就叫做抓包

requests模組

  • requests模組是一個第三方包:用於處理資料請求,模擬http請求。
  • http請求的get請求方式

    url = "http://www.baidu.com/"
    response = requests.get(url=url)

  • http請求的post請求方式

    url = "http://www.baidu.com/"
    data = {鍵:"值"}
    response = requests.get(url=url,data=data)
  • 偽造(重塑)頭部資訊:

    headers={"鍵":"值,}
    response = requests.get(url=url,headers=headers)

    超時引數

  • 配合異常捕獲,在指定時間返回響應

    # 在三秒內必須返回響應,否則則會報錯
    response = requests.get(url=url,headers=headers,timeout=3)

    response方法:

  • response.text:獲取原始碼(易出現亂碼)
    • response.encoding = response.apparent_encoding:設定伺服器的編碼為本地的編碼
  • response.content.decode():把相應的二進位制流,轉化成str型別

    # 這裡的gbk 是 可選引數
    response.content.decode("gbk")
  • response.request.url :請求的目標url地址
  • response.url :響應的url地址,這個有時候會因為跳轉而和請求目標地址不一致
  • response.request.headers 請求頭
  • response.headers 響應頭

  • requests方式比urlib方式簡單
  • 50%的動態內容:json格式的資料。

retring模組:

  • 第三方模組,用於重試異常
  • 用於重試出現異常的函式:通俗理解是,“出事了我都這,你儘管去做,做錯了也不怕,但最多三次”

    from retrying import retry
    # 作為裝飾器 裝飾函式,報錯仍然重複執行,直到三次才算出錯
    @ retry(stop_max_attempt_number=3)
    def fun1():
    print("hello world.")
    time.sleep(1)
    raise ValueError("This is a test error.")

處理cookie相關的請求

  • DEMO:人人網
  • 直接攜帶cookie,然後請求URL
    • 直接放在headers中
    headers = {"Referver":"URL","User-Agent":"瀏覽器資訊","Cookie":"..."}
    • cookie字典傳給cookie引數,這個辦法非常酷。Cookie引數只接收字典。【傳說中的字典推導式】
    # Cookie字串
    CookieStr = "anonymid=jm5lz4q0wv29sz; depovince=BJ; _r01_=1; JSESSIONID=abcpoYHrUXJZa6yQOiLxw; ick_login=4c5c0781-d37e-44c5-98a7-8e3fee81d222; ick=e74b83d4-c258-4de9-bd6c-3426f777fadd; XNESSESSIONID=949847ff1bf9; jebe_key=38dc9339-f175-472f-9288-02e4e7e4b59c%7Cd931901a60720bfd1a4f2f38c98639f3%7C1537147847212%7C1%7C1537147849534; wp_fold=0; jebecookies=054ec649-ffed-4989-beda-1be907785a33|||||; _de=BA25AEE63DF6B7A91E89E9A775AD2FBD; p=5929761e2707b622b09b23f92b3c04aa8; first_login_flag=1; ln_uact=15661592012; ln_hurl=http://head.xiaonei.com/photos/0/0/men_main.gif; t=aaf1d9103cf916e1d824260c832ceaf38; societyguester=aaf1d9103cf916e1d824260c832ceaf38; id=968054908; xnsid=1acd462c; ver=7.0; loginfrom=null"
    # 這是一個字典生成式,生成正式使用的Cookie
    Cookie = {i.split("=")[0]:i.split("=")[1] for i in CookieStr.split(";")}
    response = requests.get(url=url,headers=headers,cookies=Cookie)
  • 通過session模組獲取cookie:(這應是最常用的方法)
    1. session = requests.session() 會話保持,例項session
    2. session.post(post_url,data,headers) 很多網頁的介面可以在表單的action提交地址中看到,但有一些需要抓包才能看到
    3. response = session.get(url,headers)
    • 使用session不僅可以處理相關登陸,而且可以應對相關反爬措施。
  • 處理登陸相關的驗證碼可以找:打碼兔

資料提取方法

JSON

  • 資料交換格式,在Python的識別中其實是一個字串
  • 學習處理和操縱json資料,非常重要
  • 匯入標準庫模組jsonimport json
  • json.loads(JSON資料) Json字串過載為Python可識別的字典(dict)型別
    • 將Json字串轉化為Python容器型別(Dic,List)
    Py_data = json.loads(response.text)`
  • json.dumps(dict) 字典轉換成json字串
    • 把Python資料格式轉化成字串
    Js_data = json.dumps(Py_data)
    • dumps引數;ensure_ascii=False 取消ASC編碼
    Js_data = json.dumps(Py_data,ensure_ascii=False)
    • dumps引數;indent=2 以首行縮排兩個空格 的形式列印或儲存,美化效果
    Js_data = json.dumps(Py_data,indent=2)
  • 瀏覽器的手機模式
  • 抓包:
    • 過濾:可以通過過濾URL來大幅提高抓包效率
    • Preview:中文不會被編碼,會直接正常顯示
  • API的獲取注意:動態資料無法正常顯示,很可能是Referer導致的

Xpath與lxml

Xpath是一種提取資料的語言

  • 當使用Xpath-helper工具 定位到任何一個節點,該節點都會得到一個名為xh-highlight的class屬性做標識
  • XPath-helper外掛
  • 解析網頁內容:靜態內容(原始碼中有的)。
  • ctrl+shift+x 撥出XPath-helper
  • 按住shift移動游標,快速捕獲響應的xpath
  • 1.選擇節點物件(標籤)
    • /html/head/meta:能夠選中html下的head下的所有的meta標籤
  • 2.從任意節點開始選擇
    • //li:當前頁面所有的li標籤
    • /html/head/link//li:表示meta下的所有li標籤
  • 3.定位到具體元素 [@]
    • //div[@class="father"]//li: 獲取class名為father的div下的所有li標籤
    • 可以使用單引號
    • 在頁面中,定位id往往比定位class更合適
  • 4.獲取節點內文字 text()
    • //a/text():獲取a標籤中直接裸露的文字內容
    • a//text():獲取a下的所有文字,包括子節點文字
  • 5.選擇節點屬性內容
    • a/@href:獲取a標籤中 href屬性中的內容

      xpath方法

  • string()函式 提取多個子節點中的文字
# 獲取詞語解釋
w_ex = html2.xpath('//*[@id="content"]/div[2]')[0].xpath("string(.)")

lxml模組

  • 第三方模組,解析xml內容
  • etree.HTML方法,將響應的程式碼格式化,解析成標準的html格式。
  • @後面跟屬性,如果放在方括號中,代表對所過濾的標籤集合進行過濾。如果放在url後,則代表獲取該屬性的值

    etree.HTML(response.text)

相對路徑與絕對路徑

  • /是絕對路徑,絕對路徑的查詢難度比較大,比較麻煩
  • //是相對路徑,一般情況下我們都使用相對路徑進行查詢

mango資料庫或資料夾

  • 儲存爬取內容
  • 在xpath中的下標切片,不支援負索引,而且是從一開始,而不是從零開始的。
  • 把tbody去掉就好用了

JSON-View外掛

  • 更便於檢視JSON資料,在原來的基礎上,優化了JSON資料的顯示格式

response

  • text方法,響應的原始碼,字串型別;可以提取漢字。
  • encoding編碼

    # 將響應資訊編碼改為網站制定編碼
    response.encoding = response.apparent_encoding

檔案操作

  • os模組,用於作業系統檔案
  • 判斷資料夾是否存在

    dir = "directory"
    if not os.path.exsits(dir):
    pass
  • 建立資料夾

    dir = "directory"
    os.mkdir(dir)

  • 開啟檔案與檔案讀寫

    with("file_name","w+",encoding="utf-8") as f:
    f.write(content)

爬取動態內容與基礎反爬

  • 網頁是響應式載入,而不是一次性全部自動載入的
  • Grome瀏覽器F12:
    • Network中可以檢視請求資訊
    • Network-XHR 檢視動態請求資訊
  • user-agent/eɪdʒənt/:顯示請求瀏覽器的核心

    # 獲取返回響應資訊頭部,這個不是很重要
    for v,j in response.headers.items():
    print(v,':',j)
    # 獲取請求資訊頭部
    for v,j response.request.headers.items():
    print(v,':',j)
  • 偽造請求資訊瀏覽器核心

    headers = {
    'User-Agent':'偽造的瀏覽器核心資訊'
    }
    response = requests.get(url=url,headers=headers)
  • Referer溯源偽造
    有些網址的反爬策略是:某些網址的特定網頁會識別客戶端的refer資訊,這些資訊包含了客戶端的請求記錄。它的具體意思是,你必須經由訪問了我的主頁,跳轉到我的子頁面,而不是從URL直接過來。你先開我家門,才能到我的臥室,而不是從窗戶進來。否則就認為你是不合法的。

    headers = {
    'Referer':'偽造的 上一級URL'
    }
    response = requests.get(url=url,headers=headers)
  • 在XHR中可以找到動態資料,以及動態資料的請求介面。
  • 將json資料格式,格式化

    py_date = json.loads(response.text())

IP代理 requests.proxies

  • 相關則是隨機數模組 random

    # 代理IP列表
    proxies = [{"http":“133.12.45.12:8564”},{"協議":"IP:埠號"}]
    # random隨機選擇列表中的一個元素生成代理
    proxy = random。choice(proxies)
    response = requests.get(proxies=proxy)

MongoDB 資料庫

  • 基於分散式資料庫,非關係性資料庫中最接近關係型資料庫。
  • 庫→集合→文件
  • 伺服器端開啟服務

    mongod.exe --dbpath C:\data\db(路徑)
    指定服務地址
  • 開啟客戶端

    mongod(在環境變數下)
  • 建立資料庫

    use DB_NAME
  • 插入資料(建立集合)

    db.tb_name.insert({key:value})
  • 檢視資料庫

    show dbs;
  • 刪除資料庫

    db.dropDatabase()
  • 建立集合

    db.createCollection("set_name")
  • 檢視集合

    show collections
  • 刪除集合

    db.collection.drop()

    建立代理

  • 建立代理

    proxies = [{"http":"113.122.8.45:53128"},{"http":"114.122.8.45:53128"},{"http":"115.122.8.45:53128"}]
  • 匯入隨機數模組

    proxy = random.choice(proxies)

Retrying 模組

  • pip install retrying (安裝)
  • timeout=5 超時引數

Scrapy框架

  • 建立爬蟲的命令方式
    scrapy genspider x www.x.com
    `# 這裡使用的版本是1.0以上的版本,在填寫爬蟲域名的時候 不需要加引號
  • 執行爬蟲
    scrapy crawl x
  • 擴充套件:
    • 檢視爬蟲專案的模式列表
    >>>scrapy genspider -l 
    Available templates:
    basic
    crawl
    csvfeed
    xmlfeed
    • 使用上述模式中的一種,指定它,使用它進行開發(構建)爬蟲檔案。

spider.py檔案:

爬蟲檔案是整個框架中我們最常編寫的,裡面包括一些內容需要我們學習。

extract()函式

  • 代替etree將格式化後的原始碼內容輸出

parse()函式

  • parse() 是spider的一個方法。
  • 被呼叫時,每個初始URL完成下載後生成的 Response 物件將會作為唯一的引數傳遞給該函式。
  • 該方法負責解析返回的資料(response data),提取資料(生成item)以及生成需要進一步處理的URL的 Request 物件。

setting.py檔案:

這裡的配置有很多,但並不是我們所學的全部,譬如分散式設定,是要自己手寫進去的

  • robots.txt的規則遵循設定:這裡設定是否遵循robots檔案的協定,一般情況下設定成False就行
# Obey robots.txt rules
ROBOTSTXT_OBEY = False      # default:True
  • 設定全域性Header頭:在settings.py檔案中搜索就可以找到,取消註釋之後可以將headers頭資訊加入進去。(在DownloadMiddlewares【下載中介軟體】中也可以加headers請求頭資訊,cookie,代理等。)
# Override the default request headers:
#DEFAULT_REQUEST_HEADERS = {
#   'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
#   'Accept-Language': 'en',
#}
  • 設定爬取間隔:在制定範圍內隨機的秒數作為爬取間隔,下面這個五表示的是最大爬取間隔時間(框架會從中1~5秒中選取一個時間點),並不是每次爬取都使用這個數字作為間隔秒數。

# The initial download delay
#AUTOTHROTTLE_START_DELAY = 5
  • 開啟入庫入口檔案piplines.py的使用:在setting.py中可以找到這些程式碼,將它解開,就可以使用。同時還可以在ITEM_PIPELINES字典中設定新的內容(這些內容就是piplines.py中新的類),並且給它設定不同的優先順序。指數越低,優先順序越高。
#ITEM_PIPELINES = {
#    'Csdnboke.pipelines.CsdnbokePipeline': 300,
#}

piplines.py檔案:

一種去重方法

  • 首先建立一個新的,使用邏輯實現去重,原始碼如下:
# 匯入模組
from scrapy.exceptions import DropItem
# 建立一個用於去重的類
class Set_it(object):
    # 建立一個構造方法
    def __init__(self):
        self.set0 = set()
    # 建立一個process_item方法,它會自動呼叫
    def process_item(self, item, spider):
        # 標題
        title = item["title"]
        # 判斷標題是否已經存入了set中
        if title in self.set0:
            # 將這條item記錄刪除,並且輸出一條提示資訊
            raise DropItem("這一條內容已經存在了,所以我把它刪了:%s"%title)
        # 否則將標題存入
        else:
            # 
            self.set0.add(title)
        return item

newspaper(第三方爬蟲包)

newspaper是一個第三方包,用於快速抓取頁面資訊。它的開發團隊來自於美國,現在還仍在維護當中。如果要使用,首先要保證在你的電腦上下載並安裝這個包,對此,我們可以使用pip這樣的包管理器進行直接下載安裝,當然,用conda也可以。

  • 安裝newspaper
pip install newspaper3k
  • 在Python爬蟲檔案中匯入它
import newspaper
  • 使用例項化一個newspaper的Article方法,這個方法接收兩個引數,第一個引數是Url,第二個引數是區域語言。
# 例項化Article
# demo:
# news = newspaper.Article()
news = newspaper.Article(url=base_url,language='zh')
  • 執行Article的download方法儲存原始碼,然後執行parse方法,這兩步是目前必須的
news.download()
news.parse()
  • 獲取頁面正文
content = news.text
  • 獲取頁面標題
title = news.title
  • 獲取文章內配圖
img = news.imges

【原始碼】我自己開發了一個轉字典為sql語句的指令碼

#coding=utf-8
'''非常厲害的包 v1.0
使用說明:首先要保證你的items中的鍵值與資料庫中的欄位吻合,其次要自己建立庫和資料表,
傳入你的item,並且傳入表的名稱
form:Asterism
'''
def sql(item,table):
    '''
    item: Your dict.
    table: Your table name.
    '''
    # 生成兩個列表推導式
    keylis = [key for key,values in item.items()]
    vlis = [values for key,values in item.items()]
    # 生成sql 語句中所需要的
    str1215 = '"{}"'+',"{}"'*(len(item)-1)
    str1217 = '{}'+',{}'*(len(item)-1)
    str1216 = 'insert into '+table+'('+str1217+')'+' value ('+str1215+')'
    _sql = str1216.format(*keylis,*vlis)
    return _sql      # 返回sql語句