【學習筆記】PYTHON網絡爬蟲與信息提取(北理工 嵩天)


學習目的:
掌握定向網絡數據爬取和網頁解析的基本能力
the Website is the API…

1 python ide

文本ide:IDLE,Sublime    Text
集成ide:Pycharm,Anaconda&Spyder,Wing,Visual Studio & PTVS,Eclipse & PyDev,Canopy

默認源太慢:
阿里雲 http://mirrors.aliyun.com/pypi/simple/
中國科技大學 https://pypi.mirrors.ustc.edu.cn/simple/
豆瓣(douban) https://pypi.douban.com/simple/
清華大學 https://pypi.tuna.tsinghua.edu.cn/simple/
中國科學技術大學 https://pypi.mirrors.ustc.edu.cn/simple/

2 網絡爬蟲規則

2.1 Requests庫 自動爬取html頁面

#安裝方法 管理員權限啟動cmd安裝
pip install requests -i https://pypi.tuna.tsinghua.edu.cn/simple

#測試下image

#requests庫7個主要方法:
a、requests.request() 構造一個請求,支撐以下各方法的基礎方法
b、requests.get() 獲取HTML網頁的主要方法,對應於HTTP的GET
c、requests.head() 獲取HTML網頁頭信息的方法,對應於HTTP的HEAD
d、requests.post() 向HTML網頁提交POST請求的方法,對應於HTTP的POST
e、requests.put() 向HTML網頁提交PUT請求的方法,對應於HTTP的PUT
f、requests.patch() 向HTML網頁提交局部修改請求,對應於HTTP的PATCH
g、requests.delete() 向HTML頁面提交刪除請求,對應於HTTP的DELETE

r = requests.get(url)    
        r是返回一個包含服務器資源的Response對象,右邊是構造一個向服務器請求資源的Request對象
requests.get(url,params=None,**kwargs)  完整格式
    params:url中的額外參數,字典或者字節流格式可選
    **kwargs:12個控制訪問參數可選
打開源碼可知,get方法是調用requests方法封裝的,實際上7個方法中,其余6個都是由request方法封裝
Response對象常用5個屬性
    r.status_code HTTP請求的返回狀態,200表示連接成功,404表示失敗
    r.text HTTP響應內容的字符串形式,即,url對應的頁面內容
    r.encoding 從HTTP header中猜測的響應內容編碼方式,如果header中不存在charset,則認為編碼為ISO-8859-1
    r.apparent_encoding 從內容中分析出的響應內容編碼方式(備選編碼方式)
    r.content HTTP響應內容的二進制形式
requests庫異常
  requests.ConnectionError 網絡連接錯誤異常,如DNS查詢失敗、拒絕連接等
    requests.HTTPError HTTP錯誤異常
    requests.URLRequired URL缺失異常
    requests.TooManyRedirects 超過最大重定向次數,產生重定向異常
    requests.ConnectTimeout 連接遠程服務器超時異常
    requests.Timeout 請求URL超時,產生超時異常
爬取網頁的通用代碼框架
import requests
def getHTMLText(url):
    try:
        r = requests.get(url,timeout=30)
        r.raise_for_status()
        r.encoding = r.apparent_encoding
        return r.text
    except:
        return “產生異常”
if __name__ == “__main__”:
    url = “http://www.baidu.com”
    print(getHTMLText(url))

http協議對資源的6中操作:  
GET 請求獲取URL位置的資源
HEAD 請求獲取URL位置資源的響應消息報告,即獲得該資源的頭部信息
POST 請求向URL位置的資源后附加新的數據
PUT 請求向URL位置存儲一個資源,覆蓋原URL位置的資源
PATCH 請求局部更新URL位置的資源,即改變該處資源的部分內容
DELETE 請求刪除URL位置存儲的資源 

通過URL和命令管理資源,操作獨立無狀態,網絡通道及服務器成為了黑盒子

理解PATCH和PUT的區別:
假設URL位置有一組數據UserInfo,包括UserID、UserName等20個字段
需求:用戶修改了UserName,其他不變
. 采用PATCH,僅向URL提交UserName的局部更新請求
. 采用PUT,必須將所有20個字段一並提交到URL,未提交字段被刪除
PATCH的最主要好處:節省網絡帶寬

http協議與requests庫功能是一致的

requests.request()
requests.request(method, url, **kwargs)
method : 請求方式,對應get/put/post等7種
∙ url : 擬獲取頁面的url鏈接
∙ **kwargs: 控制訪問的參數,共13個

method : 請求方式
r = requests.request(‘GET’, url, **kwargs)
r = requests.request(‘HEAD’, url, **kwargs)
r = requests.request(‘POST’, url, **kwargs)
r = requests.request(‘PUT’, url, **kwargs)
r = requests.request(‘PATCH’, url, **kwargs)
r = requests.request(‘delete’, url, **kwargs)
r = requests.request(‘OPTIONS’, url, **kwargs)

**kwargs: 控制訪問的參數,均為可選項
params : 字典或字節序列,作為參數增加到url中
data : 字典、字節序列或文件對象,作為Request的內容
json : JSON格式的數據,作為Request的內容
headers : 字典,HTTP定制頭
cookies : 字典或CookieJar,Request中的cookie
auth : 元組,支持HTTP認證功能
files : 字典類型,傳輸文件
timeout : 設定超時時間,秒為單位
proxies : 字典類型,設定訪問代理服務器,可以增加登錄認證
allow_redirects : True/False,默認為True,重定向開關
stream : True/False,默認為True,獲取內容立即下載開關
verify : True/False,默認為True,認證SSL證書開關
cert : 本地SSL證書路徑

2.2 robots.txt 網絡爬蟲排除標准

小規模,數據量小,爬取速度不敏感,Requests庫 , 90%以上   ,   爬取網頁 玩轉網頁
中規模,數據規模較大,爬取速度敏感,Scrapy庫 ,爬取網站 爬取系列網站
大規模,搜索引擎爬取,速度關鍵 ,定制開發,爬取全網

限制網絡爬蟲:1.來源審查 2.robots協議

2.3 實戰項目

a.爬取京東某網頁imageb、爬取亞馬遜某網頁 有來源審查防爬蟲imagec.爬取百度搜索關鍵詞imaged.網絡圖片的爬取和存儲image

3 網絡爬蟲規則之提取

3.1 Beautiful Soup庫入門

#安裝

pip install beautifulsoup4image#測試下imagehtml文檔  等價於  標簽樹  等價於 BeautifulSoup類image

Beautiful Soup庫,也叫beautifulsoup4 或bs4
約定引用方式如下,即主要是用BeautifulSoup類
import bs4 from
import bs4 from BeautifulSoup

4種解析器:
soup = BeautifulSoup(‘<html>data</html>’,’html.parser’)
bs4的HTML解析器   BeautifulSoup(mk,’html.parser’)     安裝bs4庫
lxml的HTML解析器    BeautifulSoup(mk,’lxml’)             pip install lxml
lxml的XML解析器        BeautifulSoup(mk,’xml’)             pip install lxml
html5lib的解析器    BeautifulSoup(mk,’html5lib’)     pip install html5lib

BeautifulSoup類5種基本元素:
Tag 標簽,最基本的信息組織單元,分別用<>和</>標明開頭和結尾
Name 標簽的名字,<p>…</p>的名字是’p’,格式:<tag>.name
Attributes 標簽的屬性,字典形式組織,格式:<tag>.attrs
NavigableString 標簽內非屬性字符串,<>…</>中字符串,格式:<tag>.string
Comment 標簽內字符串的注釋部分,一種特殊的Comment類型

Tag 標簽:
任何存在於HTML語法中的標簽都可以用soup.<tag>訪問獲得
當HTML文檔中存在多個相同<tag>對應內容時,soup.<tag>返回第一個imageTag的name(名字):每個<tag>都有自己的名字,通過<tag>.name獲取,字符串類型imageTag的attrs(屬性):一個<tag>可以有0或多個屬性,字典類型imageTag的NavigableString:NavigableString可以跨越多個層次imageTag的Comment:Comment是一種特殊類型image

html或者xml都是樹形結構

三種遍歷方式:下行遍歷、下行遍歷、平行遍歷

BeautifulSoup類型是標簽樹的根節點
下行遍歷:
.contents 子節點的列表,將<tag>所有兒子節點存入列表
.children 子節點的迭代類型,與.contents類似,用於循環遍歷兒子節點
.descendants 子孫節點的迭代類型,包含所有子孫節點,用於循環遍歷
遍歷兒子節點
for child in soup.body.children:
    print(child)
遍歷子孫節點
for child in soup.body.descendants:
    print(child)image

標簽樹的上行遍歷
屬性說明
.parent 節點的父親標簽
.parents 節點先輩標簽的迭代類型,用於循環遍歷先輩節點
遍歷所有先輩節點,包括soup本身,所以要區別判斷image

標簽樹的平行遍歷  同一個父親下
屬性說明
.next_sibling 返回按照HTML文本順序的下一個平行節點標簽
.previous_sibling 返回按照HTML文本順序的上一個平行節點標簽
.next_siblings 迭代類型,返回按照HTML文本順序的后續所有平行節點標簽
.previous_siblings 迭代類型,返回按照HTML文本順序的前續所有平行節點標簽
遍歷后續節點
for sibling in soup.a.next_sibling:
    print(sibling)
遍歷前續節點
for sibling in soup.a.previous_sibling:
    print(sibling)image

基於bs4庫的HTML格式輸出  如何讓html頁面更加友好的輸出
bs4庫的prettify()方法
.prettify()為HTML文本<>及其內容增加更加’\n’
.prettify()可用於標簽,方法:<tag>.prettify()
bs4庫的編碼
bs4庫將任何HTML輸入都變成utf‐8編碼
Python 3.x默認支持編碼是utf‐8,解析無障礙

3.2 信息組織與提取

信息標記的三種形式:xml、json、yaml
標記后的信息可形成信息組織結構,增加了信息維度
標記的結構與信息一樣具有重要價值
標記后的信息可用於通信、存儲或展示
標記后的信息更利於程序理解和運用

文本、聲音、圖像、視頻imageimageimageimage

XML 最早的通用信息標記語言,可擴展性好,但繁瑣    Internet上的信息交互與傳遞
JSON 信息有類型,適合程序處理(js),較XML簡潔      移動應用雲端和節點的信息通信,無注釋
YAML 信息無類型,文本信息比例最高,可讀性好       各類系統的配置文件,有注釋易讀

信息提取的一般方法
從標記后的信息中提取所關注的內容xml、json、yaml
方法一:完整解析信息的標記形式,再提取關鍵信息
優點:信息解析准確
缺點:提取過程繁瑣,速度慢
方法二:無視標記形式,直接搜索關鍵信息  對信息的文本查找函數即可
優點:提取過程簡潔,速度較快
缺點:提取結果准確性與信息內容相關
融合方法:結合形式解析與搜索方法,提取關鍵信息
需要標記解析器及文本查找函數

<>.find_all(name, attrs, recursive, string, **kwargs)
返回一個列表類型,存儲查找的結果
∙ name : 對標簽名稱的檢索字符串
∙ attrs: 對標簽屬性值的檢索字符串,可標注屬性檢索
∙ recursive: 是否對子孫全部檢索,默認True
∙ string: <>…</>中字符串區域的檢索字符串

<tag>(..) 等價於<tag>.find_all(..)
soup(..) 等價於soup.find_all(..)

擴展方法:

<>.find() 搜索且只返回一個結果,同.find_all()參數
<>.find_parents() 在先輩節點中搜索,返回列表類型,同.find_all()參數
<>.find_parent() 在先輩節點中返回一個結果,同.find()參數
<>.find_next_siblings() 在后續平行節點中搜索,返回列表類型,同.find_all()參數
<>.find_next_sibling() 在后續平行節點中返回一個結果,同.find()參數
<>.find_previous_siblings() 在前序平行節點中搜索,返回列表類型,同.find_all()參數
<>.find_previous_sibling() 在前序平行節點中返回一個結果,同.find()參數image

3.3 實例:大學排名爬取

#CrawUnivRankingB.py
import requests
from bs4 import BeautifulSoup
import bs4
 
def getHTMLText(url):
    try:
        r = requests.get(url, timeout=30)
        r.raise_for_status()
        r.encoding = r.apparent_encoding
        return r.text
    except:
        return ""
 
def fillUnivList(ulist, html):
    soup = BeautifulSoup(html, "html.parser")
    for tr in soup.find('tbody').children:
        if isinstance(tr, bs4.element.Tag):
            tds = tr('td')
            ulist.append([tds[0].string, tds[1].string, tds[3].string])
 
def printUnivList(ulist, num):
    tplt = "{0:^10}\t{1:{3}^10}\t{2:^10}"
    print(tplt.format("排名","學校名稱","總分",chr(12288)))
    for i in range(num):
        u=ulist[i]
        print(tplt.format(u[0],u[1],u[2],chr(12288)))
     
def main():
    uinfo = []
    url = 'https://www.zuihaodaxue.cn/zuihaodaxuepaiming2016.html'
    html = getHTMLText(url)
    fillUnivList(uinfo, html)
    printUnivList(uinfo, 20) # 20 univs
main()

4 網絡爬蟲之實戰

4.1正則表達式

正則表達式語法由字符和操作符構成
常用操作符
. 表示任何單個字符
[ ] 字符集,對單個字符給出取值范圍[abc]表示a、b、c,[a‐z]表示a到z單個字符
[^ ] 非字符集,對單個字符給出排除范圍[^abc]表示非a或b或c的單個字符
* 前一個字符0次或無限次擴展abc* 表示ab、abc、abcc、abccc等
+ 前一個字符1次或無限次擴展abc+ 表示abc、abcc、abccc等
? 前一個字符0次或1次擴展abc? 表示ab、abc
| 左右表達式任意一個abc|def 表示abc、def
{m} 擴展前一個字符m次ab{2}c表示abbc
{m,n} 擴展前一個字符m至n次(含n) ab{1,2}c表示abc、abbc
^ 匹配字符串開頭^abc表示abc且在一個字符串的開頭
$ 匹配字符串結尾abc$表示abc且在一個字符串的結尾
( ) 分組標記,內部只能使用| 操作符(abc)表示abc,(abc|def)表示abc、def
\d 數字,等價於[0‐9]
\w 單詞字符,等價於[A‐Za‐z0‐9_]

經典正則表達式
^[A‐Za‐z]+$  由26個字母組成的字符串
^[A‐Za‐z0‐9]+$    由26個字母和數字組成的字符串
^‐?\d+$    整數形式的字符串
^[0‐9]*[1‐9][0‐9]*$    正整數形式的字符串
[1‐9]\d{5}    中國境內郵政編碼,6位
[\u4e00‐\u9fa5]    匹配中文字符
\d{3}‐\d{8}|\d{4}‐\d{7}    國內電話號碼,010‐68913536

ip地址
精確寫法0‐99: [1‐9]?\d
100‐199: 1\d{2}
200‐249: 2[0‐4]\d
250‐255: 25[0‐5]
(([1‐9]?\d|1\d{2}|2[0‐4]\d|25[0‐5]).){3}([1‐9]?\d|1\d{2}|2[0‐4]\d|25[0‐5])

raw string類型(原生字符串類型)
re庫采用raw string類型表示正則表達式,表示為:r’text’ 例如: r'[1‐9]\d{5}’      r’\d{3}‐\d{8}|\d{4}‐\d{7}’
raw string是不包含對轉義符再次轉義的字符串
re庫也可以采用string類型表示正則表達式,但更繁瑣
例如:
‘[1‐9]\\d{5}’
‘\\d{3}‐\\d{8}|\\d{4}‐\\d{7}’
建議:當正則表達式包含轉義符時,使用raw string

re庫主要功能函數
re.search() 在一個字符串中搜索匹配正則表達式的第一個位置,返回match對象
re.match() 從一個字符串的開始位置起匹配正則表達式,返回match對象
re.findall() 搜索字符串,以列表類型返回全部能匹配的子串
re.split() 將一個字符串按照正則表達式匹配結果進行分割,返回列表類型
re.finditer() 搜索字符串,返回一個匹配結果的迭代類型,每個迭代元素是match對象
re.sub() 在一個字符串中替換所有匹配正則表達式的子串,返回替換后的字符串

re.search(pattern, string, flags=0)
∙ pattern : 正則表達式的字符串或原生字符串表示
∙ string : 待匹配字符串
∙ flags : 正則表達式使用時的控制標記
常用標記
re.I re.IGNORECASE 忽略正則表達式的大小寫,[A‐Z]能夠匹配小寫字符
re.M re.MULTILINE 正則表達式中的^操作符能夠將給定字符串的每行當作匹配開始
re.S re.DOTALL 正則表達式中的.操作符能夠匹配所有字符,默認匹配除換行外的所有字符image

re.match(pattern, string, flags=0)
. pattern : 正則表達式的字符串或原生字符串表示
. string : 待匹配字符串
. flags : 正則表達式使用時的控制標記image

re.findall(pattern, string, flags=0)
. pattern : 正則表達式的字符串或原生字符串表示
. string : 待匹配字符串
. flags : 正則表達式使用時的控制標記image

re.split(pattern, string, maxsplit=0, flags=0)
. pattern : 正則表達式的字符串或原生字符串表示
. string : 待匹配字符串
. maxsplit: 最大分割數,剩余部分作為最后一個元素輸出
. flags : 正則表達式使用時的控制標記image

re.finditer(pattern, string, flags=0)
∙ pattern : 正則表達式的字符串或原生字符串表示
∙ string : 待匹配字符串
∙ flags : 正則表達式使用時的控制標記image

re.sub(pattern, repl, string, count=0, flags=0)
∙ pattern : 正則表達式的字符串或原生字符串表示
∙ repl : 替換匹配字符串的字符串
∙ string : 待匹配字符串
∙ count : 匹配的最大替換次數
∙ flags : 正則表達式使用時的控制標記image

regex = re.compile(pattern, flags=0)
∙ pattern : 正則表達式的字符串或原生字符串表示
∙ flags : 正則表達式使用時的控制標記
>>> regex = re.compile(r'[1‐9]\d{5}’)

regex.search() 在一個字符串中搜索匹配正則表達式的第一個位置,返回match對象
regex.match() 從一個字符串的開始位置起匹配正則表達式,返回match對象
regex.findall() 搜索字符串,以列表類型返回全部能匹配的子串
regex.split() 將一個字符串按照正則表達式匹配結果進行分割,返回列表類型
regex.finditer() 搜索字符串,返回一個匹配結果的迭代類型,每個迭代元素是match對象
regex.sub() 在一個字符串中替換所有匹配正則表達式的子串,返回替換后的字符串

Match對象是一次匹配的結果,包含匹配的很多信息
Match對象的屬性:
.string 待匹配的文本
.re 匹配時使用的patter對象(正則表達式)
.pos 正則表達式搜索文本的開始位置
.endpos 正則表達式搜索文本的結束位置
Match對象的方法:
.group(0) 獲得匹配后的字符串
.start() 匹配字符串在原始字符串的開始位置
.end() 匹配字符串在原始字符串的結束位置
.span() 返回(.start(), .end())image

Re庫的貪婪和最小匹配
Re庫默認采用貪婪匹配,即輸出匹配最長的子串
最小匹配操作符:
*? 前一個字符0次或無限次擴展,最小匹配
+? 前一個字符1次或無限次擴展,最小匹配
?? 前一個字符0次或1次擴展,最小匹配
{m,n}? 擴展前一個字符m至n次(含n),最小匹配

只要長度輸出可能不同的,都可以通過在操作符后增加?變成最小匹配image

4.2 淘寶商品比價定向爬蟲

import requests
import re
 
def getHTMLText(url):
    try:
        r = requests.get(url, timeout=30)
        r.raise_for_status()
        r.encoding = r.apparent_encoding
        return r.text
    except:
        return ""
     
def parsePage(ilt, html):
    try:
        plt = re.findall(r'\"view_price\"\:\"[\d\.]*\"',html)
        tlt = re.findall(r'\"raw_title\"\:\".*?\"',html)
        for i in range(len(plt)):
            price = eval(plt[i].split(':')[1])
            title = eval(tlt[i].split(':')[1])
            ilt.append([price , title])
    except:
        print("")
 
def printGoodsList(ilt):
    tplt = "{:4}\t{:8}\t{:16}"
    print(tplt.format("序號", "價格", "商品名稱"))
    count = 0
    for g in ilt:
        count = count + 1
        print(tplt.format(count, g[0], g[1]))
         
def main():
    goods = '書包'
    depth = 3
    start_url = 'https://s.taobao.com/search?q=' + goods
    infoList = []
    for i in range(depth):
        try:
            url = start_url + '&s=' + str(44*i)
            html = getHTMLText(url)
            parsePage(infoList, html)
        except:
            continue
    printGoodsList(infoList)
     
main()

4.3 股票數據定向爬蟲

import requests
from bs4 import BeautifulSoup
import traceback
import re
 
def getHTMLText(url, code="utf-8"):
    try:
        r = requests.get(url)
        r.raise_for_status()
        r.encoding = code
        return r.text
    except:
        return ""
 
def getStockList(lst, stockURL):
    html = getHTMLText(stockURL, "GB2312")
    soup = BeautifulSoup(html, 'html.parser') 
    a = soup.find_all('a')
    for i in a:
        try:
            href = i.attrs['href']
            lst.append(re.findall(r"[s][hz]\d{6}", href)[0])
        except:
            continue
 
def getStockInfo(lst, stockURL, fpath):
    count = 0
    for stock in lst:
        url = stockURL + stock + ".html"
        html = getHTMLText(url)
        try:
            if html=="":
                continue
            infoDict = {}
            soup = BeautifulSoup(html, 'html.parser')
            stockInfo = soup.find('div',attrs={'class':'stock-bets'})
 
            name = stockInfo.find_all(attrs={'class':'bets-name'})[0]
            infoDict.update({'股票名稱': name.text.split()[0]})
             
            keyList = stockInfo.find_all('dt')
            valueList = stockInfo.find_all('dd')
            for i in range(len(keyList)):
                key = keyList[i].text
                val = valueList[i].text
                infoDict[key] = val
             
            with open(fpath, 'a', encoding='utf-8') as f:
                f.write( str(infoDict) + '\n' )
                count = count + 1
                print("\r當前進度: {:.2f}%".format(count*100/len(lst)),end="")
        except:
            count = count + 1
            print("\r當前進度: {:.2f}%".format(count*100/len(lst)),end="")
            continue
 
def main():
    stock_list_url = 'https://quote.eastmoney.com/stocklist.html'
    stock_info_url = 'https://gupiao.baidu.com/stock/'
    output_file = 'D:/BaiduStockInfo.txt'
    slist=[]
    getStockList(slist, stock_list_url)
    getStockInfo(slist, stock_info_url, output_file)
 
main()

5 網絡爬蟲之框架-Scrapy

5.1 Scrapy框架介紹

Scrapy是一個快速功能強大的網絡爬蟲框架
安裝:pip install scrapy

Microsoft Visual C++ 14.0 is required…報錯,https://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted 網站下載對應版本

然后安裝pip install d:\Twisted-18.7.0-cp36-cp36m-win32.whl

之后再安裝scrapy

小測:scrapy ‐h
Scrapy不是一個函數功能庫,而是一個爬蟲框架
爬蟲框架是實現爬蟲功能的一個軟件結構和功能組件集合
爬蟲框架是一個半成品,能夠幫助用戶實現專業網絡爬蟲image

1 Engine從Spider處獲得爬取請求(Request)
2 Engine將爬取請求轉發給Scheduler,用於調度
3 Engine從Scheduler處獲得下一個要爬取的請求
4 Engine將爬取請求通過中間件發送給Downloader
5 爬取網頁后,Downloader形成響應(Response)通過中間件發給Engine
6 Engine將收到的響應通過中間件發送給Spider處理
7 Spider處理響應后產生爬取項(scraped Item)和新的爬取請求(Requests)給Engine
8 Engine將爬取項發送給Item Pipeline(框架出口)
9 Engine將爬取請求發送給Scheduler
Engine控制各模塊數據流,不間斷從Scheduler處獲得爬取請求,直至請求為空
框架入口:Spider的初始爬取請求
框架出口:Item Pipeline

Engine
(1) 控制所有模塊之間的數據流
(2) 根據條件觸發事件
不需要用戶修改

Downloader
根據請求下載網頁
不需要用戶修改

Scheduler
對所有爬取請求進行調度管理
不需要用戶修改

Downloader Middleware
目的:實施Engine、Scheduler和Downloader之間進行用戶可配置的控制
功能:修改、丟棄、新增請求或響應
用戶可以編寫配置代碼

Spider
(1) 解析Downloader返回的響應(Response)
(2) 產生爬取項(scraped item)
(3) 產生額外的爬取請求(Request)
需要用戶編寫配置代碼

Item Pipelines
(1) 以流水線方式處理Spider產生的爬取項
(2) 由一組操作順序組成,類似流水線,每個操作是一個Item Pipeline類型
(3) 可能操作包括:清理、檢驗和查重爬取項中的HTML數據、將數據存儲到數據庫
需要用戶編寫配置代碼

Spider Middleware
目的:對請求和爬取項的再處理
功能:修改、丟棄、新增請求或爬取項
用戶可以編寫配置代碼

requests vs. Scrapy

相同點:
兩者都可以進行頁面請求和爬取,Python爬蟲的兩個重要技術路線
兩者可用性都好,文檔豐富,入門簡單
兩者都沒有處理js、提交表單、應對驗證碼等功能(可擴展)

requests 庫:
頁面級爬蟲
功能庫
並發性考慮不足,性能較差
重點在於頁面下載
定制靈活
上手十分簡單

Scrapy框架:
網站級爬蟲
框架
並發性好,性能較高
重點在於爬蟲結構
一般定制靈活,深度定制困難
入門稍難requests

Scrapy是為持續運行設計的專業爬蟲框架,提供操作的Scrapy命令行:scrapy <command> [options] [args]

常用命令:
startproject 創建一個新工程scrapy startproject <name> [dir]
genspider 創建一個爬蟲scrapy genspider [options] <name> <domain>
settings 獲得爬蟲配置信息scrapy settings [options]
crawl 運行一個爬蟲scrapy crawl <spider>
list 列出工程中所有爬蟲scrapy list
shell 啟動URL調試命令行scrapy shell [url]

5.2 Scrapy爬蟲基本使用

demo.py


import scrapy
class DemoSpider(scrapy.Spider):
    name = "demo"
    #allowed_domains = ["python123.io"]
    start_urls = ['https://python123.io/ws/demo.html']
    def parse(self, response):
        fname = response.url.split('/')[-1]
        with open(fname, 'wb') as f:
            f.write(response.body)
        self.log('Saved file %s.' % name)

步驟1:建立一個Scrapy爬蟲工程 scrapy startproject python123demoimage

步驟2:在工程中產生一個Scrapy爬蟲
進入demo目錄中運行 scrapy genspider dem python123.io

該命令作用:
(1) 生成一個名稱為demo的spider
(2) 在spiders目錄下增加代碼文件demo.py
該命令僅用於生成demo.py,該文件也可以手工生成

步驟3:配置產生的spider爬蟲
配置:(1)初始URL地址(2)獲取頁面后的解析方式

步驟4:運行爬蟲,獲取網頁
在命令行下,執行如下命令:scrapy crawl demo
demo爬蟲被執行,捕獲頁面存儲在demo.htmlimage

yield 生成器

包含yield語句的函數是一個生成器
生成器每次產生一個值(yield語句),函數被凍結,被喚醒后再產生一個值
生成器是一個不斷產生值的函數
生成器每調用一次在yield位置產生一個值,直到函數執行結束imageimage

Scrapy爬蟲的數據類型:

Request類  class scrapy.http.Request() Request對象表示一個HTTP請求 由Spider生成,由Downloader執行
.url Request對應的請求URL地址
.method 對應的請求方法,’GET’ ‘POST’等
.headers 字典類型風格的請求頭
.body 請求內容主體,字符串類型
.meta 用戶添加的擴展信息,在Scrapy內部模塊間傳遞信息使用
.copy() 復制該請求

Response類 class scrapy.http.Response()  Response對象表示一個HTTP響應 由Downloader生成,由Spider處理
.url Response對應的URL地址
.status HTTP狀態碼,默認是200
.headers Response對應的頭部信息
.body Response對應的內容信息,字符串類型
.flags 一組標記
.request 產生Response類型對應的Request對象
.copy() 復制該響應

Item類class scrapy.item.Item()

Item對象表示一個從HTML頁面中提取的信息內容
由Spider生成,由Item Pipeline處理
Item類似字典類型,可以按照字典類型操作

Scrapy爬蟲支持多種HTML信息提取方法:
• Beautiful Soup
• lxml
• re
• XPath Selector
• CSS Selector

5.3 股票數據爬蟲scrapy實例

scrapy startproject BaiduStocks

scrapy genspider stocks baidu.com

stocks.py文件源代碼

# -*- coding: utf-8 -*-
import scrapy
import re
 
 
class StocksSpider(scrapy.Spider):
    name = "stocks"
    start_urls = ['https://quote.eastmoney.com/stocklist.html']
 
    def parse(self, response):
        for href in response.css('a::attr(href)').extract():
            try:
                stock = re.findall(r"[s][hz]\d{6}", href)[0]
                url = 'https://gupiao.baidu.com/stock/' + stock + '.html'
                yield scrapy.Request(url, callback=self.parse_stock)
            except:
                continue
 
    def parse_stock(self, response):
        infoDict = {}
        stockInfo = response.css('.stock-bets')
        name = stockInfo.css('.bets-name').extract()[0]
        keyList = stockInfo.css('dt').extract()
        valueList = stockInfo.css('dd').extract()
        for i in range(len(keyList)):
            key = re.findall(r'>.*', keyList[i])[0][1:-5]
            try:
                val = re.findall(r'\d+\.?.*', valueList[i])[0][0:-5]
            except:
                val = '--'
            infoDict[key]=val
 
        infoDict.update(
            {'股票名稱': re.findall('\s.*\(',name)[0].split()[0] + \
             re.findall('\>.*\<', name)[0][1:-1]})
        yield infoDict




下面是pipelines.py文件源代碼:

# -*- coding: utf-8 -*-
 
# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://doc.scrapy.org/en/latest/topics/item-pipeline.html
 
 
class BaidustocksPipeline(object):
    def process_item(self, item, spider):
        return item
 
class BaidustocksInfoPipeline(object):
    def open_spider(self, spider):
        self.f = open('BaiduStockInfo.txt', 'w')
 
    def close_spider(self, spider):
        self.f.close()
 
    def process_item(self, item, spider):
        try:
            line = str(dict(item)) + '\n'
            self.f.write(line)
        except:
            pass
        return item

下面是settings.py文件中被修改的區域:

# Configure item pipelines
# See https://scrapy.readthedocs.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
    'BaiduStocks.pipelines.BaidustocksInfoPipeline': 300,
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM