爬蟲 + 數據

爬蟲 + 數據 - day01

啓動:jupyter notebook
介紹:
anaconda是一個集成環境(數據分析+機器學習)
提供了一個叫作jupyter的可視化工具(基於瀏覽器)
jupyter的基本使用
快捷鍵:
插入cell:a,b
刪除:x
執行:shift+enter
切換cell的模式:y,m
tab:自動補全
打開幫助文檔:shift+tab

1. 什麼是爬蟲 :

  • 經過編寫程序模擬瀏覽器上網,從互聯網中爬取須要的數據的過程

2. 爬蟲的分類 :

  • 通用爬蟲 : 爬取一整張頁面源碼數據.搜索引擎 (抓取系統→內部封好的一套爬蟲程序) 重點使用的是該種形式爬蟲
  • 聚焦爬蟲 : 抓取頁面中指定的局部數據
  • 增量式爬蟲 : 監測網站數據更新的狀況.抓取網站最新更新的數據

3. 爬蟲安全性的探究

  • 風險所在php

    • 爬蟲干擾了被訪問網站的正常運營;
    • 爬蟲抓取了受到法律保護的特定類型的數據或信息
  • 如何規避風險css

    • 嚴格遵照網站設置的robots協議;
    • 在規避反爬蟲措施的同時,須要優化本身的代碼,避免干擾被訪問網站的正常運行;
    • 在使用、傳播抓取到的信息時,應審查所抓取的內容,如發現屬於用戶的我的信息、隱私或者他人的商業祕密的,應及時中止並刪除

    爬蟲機制 :應用在網站中html

    反反爬機制 : 應用在爬蟲程序中python

    第一個反爬機制 :web

    ​ robots協議:純文本的協議面試

    • 特色:防君子不防小人

4. http & https

  • 什麼是http協議
    • 服務器和客戶端進行數據交互的某種形式
  • https - 安全 (數據加密) 的http協議

頭部信息

一、通用頭部

通用頭域包含請求和響應消息都支持的頭域。ajax

Request URL:請求的URL地址
Request Method: 請求方法,get/post/put/……
Status Code:狀態碼,200 爲請求成功
Remote Address:路由地址

二、請求頭部

1) Accept:  告訴WEB服務器本身接受什麼介質類型,*/* 表示任何類型,type/* 表示該類型下的全部子類型;
2)Accept-Charset:  瀏覽器申明本身接收的字符集
  Accept-Encoding:瀏覽器申明本身接收的編碼方法,一般指定壓縮方法,是否支持壓縮,支持什麼壓縮方法(gzip,     deflate)
3)Accept-Language:  瀏覽器申明本身接收的語言。語言跟字符集的區別:中文是語言,中文有多種字符集,好比big5,gb2312,gbk等等。
4)Authorization:  當客戶端接收到來自WEB服務器的 WWW-Authenticate 響應時,該頭部來回應本身的身份驗證信息給WEB服務器。
5)Connection:表示是否須要持久鏈接。close(告訴WEB服務器或者代理服務器,在完成本次請求的響應後,斷開鏈接,
     不要等待本次鏈接的後續請求了)。keep-alive(告訴WEB服務器或者代理服務器,在完成本次請求的響應後,保持鏈接,等待本次鏈接的後續請求)。
6)Referer:發送請求頁面URL。瀏覽器向 WEB 服務器代表本身是從哪一個 網頁/URL 得到/點擊 當前請求中的網址/URL。
7)User-Agent: 瀏覽器代表本身的身份(是哪一種瀏覽器)。
8)Host: 發送請求頁面所在域。
9)Cache-Control:瀏覽器應遵循的緩存機制。
       no-cache(不要緩存的實體,要求如今從WEB服務器去取)
       max-age:(只接受 Age 值小於 max-age 值,而且沒有過時的對象) 
       max-stale:(能夠接受過去的對象,可是過時時間必須小於 max-stale 值)  
       min-fresh:(接受其新鮮生命期大於其當前 Age 跟 min-fresh 值之和的緩存對象)
10)Pramga:主要使用 Pramga: no-cache,至關於 Cache-Control: no-cache。
11)Range:瀏覽器(好比 Flashget 多線程下載時)告訴 WEB 服務器本身想取對象的哪部分。
12)Form:一種請求頭標,給定控制用戶代理的人工用戶的電子郵件地址。
13)Cookie:這是最重要的請求頭信息之一

三、響應頭部

1)Age:當代理服務器用本身緩存的實體去響應請求時,用該頭部代表該實體從產生到如今通過多長時間了。
2)Accept-Ranges:WEB服務器代表本身是否接受獲取其某個實體的一部分(好比文件的一部分)的請求。bytes:表示接受,none:表示不接受。
3) Cache-Control:服務器應遵循的緩存機制。
    public(能夠用 Cached 內容迴應任何用戶)
    private(只能用緩存內容迴應先前請求該內容的那個用戶)
    no-cache(能夠緩存,可是隻有在跟WEB服務器驗證了其有效後,才能返回給客戶端) 
    max-age:(本響應包含的對象的過時時間)  
    ALL:  no-store(不容許緩存)  
4) Connection: 是否須要持久鏈接
        close(鏈接已經關閉)。
        keepalive(鏈接保持着,在等待本次鏈接的後續請求)。
        Keep-Alive:若是瀏覽器請求保持鏈接,則該頭部代表但願 WEB 服務器保持鏈接多長時間(秒)。例如:Keep-                     Alive:300
5)Content-Encoding:WEB服務器代表本身使用了什麼壓縮方法(gzip,deflate)壓縮響應中的對象。 例如:Content-Encoding:gzip 
6)Content-Language:WEB 服務器告訴瀏覽器本身響應的對象的語言。
7)Content-Length:WEB 服務器告訴瀏覽器本身響應的對象的長度。例如:Content-Length: 26012
8)Content-Range:WEB 服務器代表該響應包含的部分對象爲整個對象的哪一個部分。例如:Content-Range: bytes 21010-47021/47022
9)Content-Type:WEB 服務器告訴瀏覽器本身響應的對象的類型。例如:Content-Type:application/xml
10)Expired:WEB服務器代表該實體將在何時過時,對於過時了的對象,只有在跟WEB服務器驗證了其有效性後,才能用來響應客戶請求。
11) Last-Modified:WEB 服務器認爲對象的最後修改時間,好比文件的最後修改時間,動態頁面的最後產生時間等等。
12) Location:WEB 服務器告訴瀏覽器,試圖訪問的對象已經被移到別的位置了,到該頭部指定的位置去取。
13)Proxy-Authenticate: 代理服務器響應瀏覽器,要求其提供代理身份驗證信息。
14)Server: WEB 服務器代表本身是什麼軟件及版本等信息。
15)Refresh:表示瀏覽器應該在多少時間以後刷新文檔,以秒計。

https的加密方式

對稱密鑰加密
非對稱密鑰加密
證書密鑰加密

5. request模塊

基於網絡請求的python模塊json

做用 :模擬瀏覽器發送請求,實現爬蟲flask

環境安裝 : pip install requestapi

編碼流程 :

  • 指定url
  • 發起請求
  • 獲取響應數據
  • 持久化存儲

1. 爬取搜狗首頁的頁面源碼數據

import requests
#1.指定url
url = 'https://www.sogou.com/'
#2.請求發送:get返回的是一個響應對象
response = requests.get(url=url)
#3.獲取響應數據:text返回的是字符串形式的響應數據
page_text = response.text
#4.持久化存儲
with open('./sogou.html','w',encoding='utf-8') as fp:
    fp.write(page_text)

2. 實現一個簡易的網頁採集器

請求參數的動態化

url = 'https://www.sogou.com/web'
#請求參數的動態化
wd = input('enter a key word:')
params = {
    'query':wd
}
response = requests.get(url=url,params=params)
page_text = response.text
fileName = wd+'.html'
with open(fileName,'w',encoding='utf-8') as fp:
    fp.write(page_text)
print(fileName,'爬取成功!')

上述代碼問題:

  • 亂碼問題
    • response.encoding = 'xxx'
  • 數據丟失
    • 反爬機制:UA檢測
    • 反反爬策略:UA假裝
#亂碼問題的解決
url = 'https://www.sogou.com/web'
#請求參數的動態化
wd = input('enter a key word:')
params = {
    'query':wd
}

response = requests.get(url=url,params=params)

#將響應數據的編碼格式手動進行指定
response.encoding = 'utf-8'
page_text = response.text
fileName = wd+'.html'
with open(fileName,'w',encoding='utf-8') as fp:
    fp.write(page_text)
print(fileName,'爬取成功!')
#UA假裝操做
url = 'https://www.sogou.com/web'
#請求參數的動態化
wd = input('enter a key word:')
params = {
    'query':wd
}

#UA假裝
headers = {
    'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'
}
response = requests.get(url=url,params=params,headers=headers)

#將響應數據的編碼格式手動進行指定
response.encoding = 'utf-8'
page_text = response.text
fileName = wd+'.html'
with open(fileName,'w',encoding='utf-8') as fp:
    fp.write(page_text)
print(fileName,'爬取成功!')

3. 動態加載的數據

經過另外一個網絡請求 (ajax) 請求到的數據

爬取豆瓣電影中動態加載出的電影詳情數據 :

url = 'https://movie.douban.com/j/chart/top_list'
#參數動態化
params = {
    'type': '17',
    'interval_id': '100:90',
    'action': '',
    'start': '0',
    'limit': '200',
}
response = requests.get(url=url,params=params,headers=headers)
#json()返回的是序列化好的對象
movie_list = response.json()
for movie in movie_list:
    print(movie['title'],movie['score'])
總結:對一個陌生的網站進行數據爬取的時候,首先肯定的一點就是爬取的數據是否爲動態加載出來的
    是:須要經過抓包工具捕獲到動態加載數據對應的數據包,從中提取出url和請求參數。
    不是:直接對瀏覽器地址欄的url發起請求便可
如何檢測爬取的數據是否是動態加載出來的?
    經過抓包工具進行局部搜索就能夠驗證數據是否爲動態加載
        搜索到:不是動態加載
        搜索不到:是動態加載
如何定位動態加載的數據在哪呢?
    經過抓包工具進行全局搜索進行定位

4. 爬取肯德基的餐廳位置信息

http://www.kfc.com.cn/kfccda/storelist/index.aspx

url = 'http://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx?op=keyword'
data = {
    'cname': '',
    'pid': '',
    'keyword': '上海',
    'pageIndex': '1',
    'pageSize': '10',
}
address_dic = requests.post(url=url,data=data,headers=headers).json()
for dic in address_dic['Table1']:
    print(dic['addressDetail'])

5. 面試題

- 需求
https://www.fjggfw.gov.cn/Website/JYXXNew.aspx 福建省公共資源交易中心
提取內容:
工程建設中的中標結果信息/中標候選人信息
1. 完整的html中標信息
2. 第一中標候選人
3. 中標金額
4. 中標時間
5. 其它參與投標的公司
- 實現思路
    - 確認爬取的數據都是動態加載出來的
    - 在首頁中捕獲到ajax請求對應的數據包,從該數據包中提取出請求的url和請求參數
    - 對提取到的url進行請求發送,獲取響應數據(json)
    - 從json串中提取到每個公告對應的id值
    - 將id值和中標信息對應的url進行整合,進行請求發送捕獲到每個公告對應的中標信息數據
post_url = 'https://www.fjggfw.gov.cn/Website/AjaxHandler/BuilderHandler.ashx'
headers = {
    'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36',
    'Cookie': '_qddac=4-3-1.4euvh3.x4wulp.k1hj8mnw; ASP.NET_SessionId=o4xkycpib3ry5rzkvfcamxzk; Hm_lvt_94bfa5b89a33cebfead2f88d38657023=1570520304; __root_domain_v=.fjggfw.gov.cn; _qddaz=QD.89mfu7.7kgq8w.k1hj8mhg; _qdda=4-1.4euvh3; _qddab=4-x4wulp.k1hj8mnw; _qddamta_2852155767=4-0; _qddagsx_02095bad0b=2882f90558bd014d97adf2d81c54875229141367446ccfed2b0c8913707c606ccf30ec99a338fed545821a5ff0476fd6332b8721c380e9dfb75dcc00600350b31d85d17d284bb5d6713a887ee73fa35c32b7350c9909379a8d9f728ac0c902e470cb5894c901c4176ada8a81e2ae1a7348ae5da6ff97dfb43a23c6c46ec8ec10; Hm_lpvt_94bfa5b89a33cebfead2f88d38657023=1570520973'
}
data = {
    'OPtype': 'GetListNew',
    'pageNo': '1',
    'pageSize': '10',
    'proArea': '-1',
    'category': 'GCJS',
    'announcementType': '-1',
    'ProType': '-1',
    'xmlx': '-1',
    'projectName': '',
    'TopTime': '2019-07-10 00:00:00',
    'EndTime': '2019-10-08 23:59:59',
    'rrr': '0.7293828344656237',
}
post_data = requests.post(url=post_url,headers=headers,data=data).json()
for dic in post_data['data']:
    _id = int(dic['M_ID'])
    detail_url = 'https://www.fjggfw.gov.cn/Website/AjaxHandler/BuilderHandler.ashx?OPtype=GetGGInfoPC&ID={}&GGTYPE=5&url=AjaxHandler%2FBuilderHandler.ashx'.format(_id)
    company_data = requests.get(url=detail_url,headers=headers).json()['data']
    company_str = ''.join(company_data)
    print(company_str)

6. 數據解析

1. 如何爬取圖片數據?

- 基於requests|
- 基於urllib
- 區別:urllib中的urlretrieve不能夠進行UA假裝
import requests
headers = {
    'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'
}
#基於requests的圖片爬取
url = 'http://tva1.sinaimg.cn/mw600/007QUzsKgy1g7qzr59hk7j30cs0gxn82.jpg'
img_data = requests.get(url=url,headers=headers).content #content返回的是byte類型的響應數據
with open('./123.jpg','wb') as fp:
    fp.write(img_data)
#基於urllib的圖片爬取
from urllib import request
url = 'http://tva1.sinaimg.cn/mw600/007QUzsKgy1g7qzr59hk7j30cs0gxn82.jpg'
request.urlretrieve(url,'./456.jpg')

2. 數據解析

數據解析

  • 概念:將一整張頁面中的局部數據進行提取/解析

  • 做用:用來實現聚焦爬蟲的吧

  • 實現方式:

    • 正則
    • bs4
    • xpath
    • pyquery
  • 數據解析的通用原理是什麼?

    • 標籤的定位
    • 數據的提取
  • 頁面中的相關的字符串的數據都存儲在哪裏呢?

    • 標籤中間
    • 標籤的屬性中
  • - 基於聚焦爬蟲的編碼流程
        - 指定url
        - 發起請求
        - 獲取響應數據
        - 數據解析
        - 持久化存儲

正則解析

- 將煎蛋網中的圖片數據進行爬取且存儲在本地 :

import re
import os

dirName = './imgLibs'
if not os.path.exists(dirName):
    os.mkdir(dirName)
    
url = 'http://jandan.net/pic/MjAxOTEwMDktNjY=#comments'
page_text = requests.get(url,headers=headers).text
#解析數據:img標籤的src的屬性值
ex = '<div class="text">.*?<img src="(.*?)" referrerPolicy.*?</div>'
img_src_list = re.findall(ex,page_text,re.S)
for src in img_src_list:
    if 'org_src' in src:
        src = re.findall('org_src="(.*?)" onload',src)[0]
    src = 'http:'+src
    imgName = src.split('/')[-1]
    imgPath = dirName+'/'+imgName
    request.urlretrieve(src,imgPath)
    print(imgName,'下載成功!!!')

bs4解析

  • - 環境的安裝:
      - pip install bs4
      - pip install lxml
    - bs4的解析原理:
      - 實例化一個BeautifulSoup的一個對象,把即將被解析的頁面源碼數據加載到該對象中
      - 須要調用BeautifulSoup對象中的相關的方法和屬性進行標籤訂位和數據的提取
    - BeautifulSoup的實例化
      - BeautifulSoup(fp,'lxml'):將本地存儲的html文檔中的頁面源碼數據加載到該對象中
      - BeautifulSoup(page_text,'lxml'):將從互聯網中請求道的頁面源碼數據加載到改對象中
    - 標籤的定位
      - soup.tagName:只能夠定位到第一個tagName標籤
      - 屬性定位:soup.find('tagName',attrName='value'),只能夠定位到符合要求的第一個標籤
        - findAll:返回值是一個列表。能夠定位到符合要求的全部標籤
      - 選擇器定位:soup.select('選擇器')
        - 選擇器:id,class,tag,層級選擇器(大於號表示一個層級,空格表示多個層級)
    - 取文本
      - text:將標籤中全部的文本取出
      - string:將標籤中直系的文本取出
    - 取屬性
      - tag['attrName']
from bs4 import BeautifulSoup
fp = open('./test.html',encoding='utf-8')
soup = BeautifulSoup(fp,'lxml')
# soup.div
# soup.find('div',class_='song')
# soup.findAll('div',class_='song')
# soup.select('#feng')[0]
# soup.select('.tang > ul > li > a')
# soup.select('.tang a')
# tag = soup.b
# tag.string
# div_tag = soup.find('div',class_='tang')
# div_tag.text
a_tag = soup.select('#feng')[0]
a_tag

- 使用bs4解析三國演義小說的標題和內容,存儲到本地 :

main_url = 'http://www.shicimingju.com/book/sanguoyanyi.html'
page_text = requests.get(url=main_url,headers=headers).text
#數據解析:章節的標題和詳情頁的url
soup = BeautifulSoup(page_text,'lxml')
a_list = soup.select('.book-mulu > ul > li > a')
fp = open('./sanguo.txt','w',encoding='utf-8')
for a in a_list:
    title = a.string
    detail_url = 'http://www.shicimingju.com'+a['href']
    detail_page_text = requests.get(url=detail_url,headers=headers).text
    #數據解析:章節內容
    detail_soup = BeautifulSoup(detail_page_text,'lxml')
    div_tag = detail_soup.find('div',class_='chapter_content')
    content = div_tag.text
    
    fp.write(title+':'+content+'\n')
    print(title,'寫入成功!!!')
fp.close()

xpath解析

  • - 環境的安裝
      - pip install lxml
    - 解析原理
      - 實例化一個etree的對象,且把即將被解析的頁面源碼數據加載到該對象中
      - 調用etree對象中的xpath方法結合這不一樣形式的xpath表達式進行標籤訂位和數據提取
    - etree對象的實例化
      - etree.parse('fileName')  - 本地文檔
      - etree.HTML(page_text) - 網絡請求
    - 標籤訂位
      - 最左側的/:必定要從根標籤開始進行標籤訂位
      - 非最左側的/:表示一個層級
      - 最左側的//:能夠從任意位置進行指定標籤的定位
      - 非最左側的//:表示多個層級
      - 屬性定位://tagName[@attrName="value"]
      - 索引定位://tagName[@attrName="value"]/li[2],索引是從1開始
      - 邏輯運算:
        - 找到href屬性值爲空且class屬性值爲du的a標籤
        - //a[@href="" and @class="du"]
      - 模糊匹配:
        - //div[contains(@class, "ng")]
        - //div[starts-with(@class, "ta")]
    - 取文本
      - /text():直系的文本內容
      - //text():全部的文本內容
    - 取屬性
      - /@attrName
from lxml import etree
tree = etree.parse('./test.html')
# tree.xpath('/html//title')
# tree.xpath('//div')
# tree.xpath('//div[@class="tang"]')
# tree.xpath('//div[@class="tang"]/ul/li[2]')
# tree.xpath('//p[1]/text()')
# tree.xpath('//div[@class="song"]//text()')
tree.xpath('//img/@src')[0]
  • 需求:爬取虎牙主播名稱,熱度和標題
url = 'https://www.huya.com/g/xingxiu'
page_text = requests.get(url=url,headers=headers).text

#數據解析
tree = etree.HTML(page_text)
li_list = tree.xpath('//div[@class="box-bd"]/ul/li')
for li in li_list:
    #實現的是頁面局部數據的指定數據的解析
    title = li.xpath('./a[2]/text()')[0]
    author = li.xpath('./span/span[1]/i/text()')[0]
    hot = li.xpath('./span/span[2]/i[2]/text()')[0]
    
    print(title,author,hot)
  • 爬取http://pic.netbian.com/4kmeinv/中前五頁的圖片數據
    • 中文亂碼的處理
    • 多頁碼數據的爬取
# url = 'http://pic.netbian.com/4kmeinv/' #第一頁
#指定一個通用的url模板:不可變的
url = 'http://pic.netbian.com/4kmeinv/index_%d.html'
dirName = './MZLib'
if not os.path.exists(dirName):
    os.mkdir(dirName)
    
for page in range(1,6):
    if page == 1:
        new_url = 'http://pic.netbian.com/4kmeinv/'
    else:
        new_url = format(url%page)
    page_text = requests.get(url=new_url,headers=headers).text
    #數據解析:圖片地址&圖片名稱
    tree = etree.HTML(page_text)
    li_list = tree.xpath('//div[@class="slist"]/ul/li')
    for li in li_list:
        img_name = li.xpath('./a/img/@alt')[0]
        img_name = img_name.encode('iso-8859-1').decode('gbk')+'.jpg'
        img_src = 'http://pic.netbian.com'+li.xpath('./a/img/@src')[0]
        img_data = requests.get(img_src,headers=headers).content #圖片的二進制類型數據
        img_path = dirName+'/'+img_name
        with open(img_path,'wb') as fp:
            fp.write(img_data)
    print('第{}頁爬取完畢!!!'.format(page))
  • 爬取全國城市的名稱
    • https://www.aqistudy.cn/historydata/
url = 'https://www.aqistudy.cn/historydata/'
page_text = requests.get(url,headers=headers).text
tree = etree.HTML(page_text)
# hot_cities = tree.xpath('//div[@class="bottom"]/ul/li/a/text()')
# all_cities = tree.xpath('//div[@class="bottom"]/ul/div[2]/li/a/text()')
tree.xpath('//div[@class="bottom"]/ul/div[2]/li/a/text() | //div[@class="bottom"]/ul/li/a/text()')

7. 代理

代理指的就是代理服務器
代理的做用 : 
    請求和響應數據的轉發
代理和爬蟲之間的關聯 :
    能夠基於代理實現更換爬蟲程序請求的ip地址
代理網站 :
    1. 西祠 https://www.xicidaili.com/nn/
    2. 快代理
    3. www.goubanjia.comm
    4. 代理精靈 http://http.zhiliandaili.cn/
代理的匿名度 :
    高匿 : 所訪問的服務器察覺不到是不是代理訪問,也沒法知曉真正訪問的ip
    匿名 : 所訪問的服務器知道是代理訪問,但沒法查到真正的ip
    透明 : 知道是代理,而且知道真實ip
類型 :
    http
    https
# 使用代理髮請求 
import requests
headers = {
    'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36',
    'Connection':'close'
}
url = 'https://www.baidu.com/s?ie=UTF-8&wd=ip'
page_text = requests.get(url,headers=headers,proxies={'https':'125.87.99.237:22007'}).text
with open('./ip.html','w',encoding='utf-8') as fp:
    fp.write(page_text)
  • 搭建一個免費的代理池 (利用付費代理ip爬取免費代理網站的ip)
#構建一個付費的代理池
import random
ips_pool = []
url = 'http://ip.11jsq.com/index.php/api/entry?method=proxyServer.generate_api_url&packid=1&fa=0&fetch_key=&groupid=0&qty=103&time=1&pro=&city=&port=1&format=html&ss=5&css=&dt=1&specialTxt=3&specialJson=&usertype=2'
page_text = requests.get(url,headers=headers).text
tree = etree.HTML(page_text)
ip_list = tree.xpath('//body//text()')
for ip in ip_list:
    dic = {'https':ip}
    ips_pool.append(dic)

from lxml import etree
url = 'https://www.xicidaili.com/nn/%d' #通用的url模板(不可變)
all_ips = []
for page in range(1,5):
    new_url = format(url%page)
    page_text = requests.get(new_url,headers=headers,proxies=random.choice(ips_pool)).text
    tree = etree.HTML(page_text)
    #在xpath表達式中不能夠出現tbody標籤
    tr_list = tree.xpath('//*[@id="ip_list"]//tr')[1:]
    for tr in tr_list:
        ip = tr.xpath('./td[2]/text()')[0]
        port = tr.xpath('./td[3]/text()')[0]
        type_ip = tr.xpath('./td[6]/text()')[0]
        dic = {
            'ip':ip,
            'port':port,
            'type':type_ip
        }
        all_ips.append(dic)
                
print(len(all_ips))

8. cookie

需求:將https://xueqiu.com/中的新聞數據進行爬取
爬蟲中處理cookie的操做
    手動處理:將cookie寫在headers中
    自動處理:session對象。
獲取session對象:requests.Session()
做用:
    session對象和requests對象均可以對指定的url進行請求發送。只不過使用session進行請求發送的過程當中若是產生了cookie則cookie會被自動存儲在session對象中
url = 'https://xueqiu.com/v4/statuses/public_timeline_by_category.json?since_id=-1&max_id=20352188&count=15&category=-1'

news_json = requests.get(url,headers=headers).json()
news_json

#基於cookie操做的修正
session = requests.Session()
url = 'https://xueqiu.com/v4/statuses/public_timeline_by_category.json?since_id=-1&max_id=20352188&count=15&category=-1'
#將cookie存儲到session中,目的是將cookie獲取存儲到session中
session.get('https://xueqiu.com/',headers=headers) 

#保證該次請求時攜帶對應的cookie才能夠請求成功
news_json = session.get(url,headers=headers).json()
news_json

9. 驗證碼的識別

使用線上的打碼平臺進行自動的識別:
    - 雲打碼
    - 超級鷹 :
        - 註冊《用戶中心》身份的帳戶
        - 登錄
            - 建立一個軟件
            - 下載示例代碼《開發文檔》
import requests
from hashlib import md5

class Chaojiying_Client(object):

    def __init__(self, username, password, soft_id):
        self.username = username
        password =  password.encode('utf8')
        self.password = md5(password).hexdigest()
        self.soft_id = soft_id
        self.base_params = {
            'user': self.username,
            'pass2': self.password,
            'softid': self.soft_id,
        }
        self.headers = {
            'Connection': 'Keep-Alive',
            'User-Agent': 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)',
        }

    def PostPic(self, im, codetype):
        """
        im: 圖片字節
        codetype: 題目類型 參考 http://www.chaojiying.com/price.html
        """
        params = {
            'codetype': codetype,
        }
        params.update(self.base_params)
        files = {'userfile': ('ccc.jpg', im)}
        r = requests.post('http://upload.chaojiying.net/Upload/Processing.php', data=params, files=files, headers=self.headers)
        return r.json()

    def ReportError(self, im_id):
        """
        im_id:報錯題目的圖片ID
        """
        params = {
            'id': im_id,
        }
        params.update(self.base_params)
        r = requests.post('http://upload.chaojiying.net/Upload/ReportError.php', data=params, headers=self.headers)
        return r.json()
    
chaojiying = Chaojiying_Client('bobo328410948', 'bobo328410948', '899370')  #用戶中心>>軟件ID 生成一個替換 96001
im = open('a.jpg', 'rb').read()                                                 #本地圖片文件路徑 來替換 a.jpg 有時WIN系統需要//
print(chaojiying.PostPic(im,1004)['pic_str'])   




#驗證碼識別函數的封裝
def transformCode(imgPath,imgType):
    chaojiying = Chaojiying_Client('bobo328410948', 'bobo328410948', '899370')
    im = open(imgPath, 'rb').read()
    return chaojiying.PostPic(im,imgType)['pic_str']

模擬登錄

版本一 :

版本一的問題 :

請求須要有動態的參數

一般請狀況下動態變化的請求參數都會被隱藏在前臺頁面源碼中

from urllib import request

#驗證碼的識別:將驗證碼下載到本地而後提交給打嗎平臺進行識別
main_url = 'https://so.gushiwen.org/user/login.aspx?from=http://so.gushiwen.org/user/collect.aspx'
page_text = requests.get(main_url,headers=headers).text
tree = etree.HTML(page_text)
code_src = 'https://so.gushiwen.org'+tree.xpath('//*[@id="imgCode"]/@src')[0]
request.urlretrieve(code_src,'./code.jpg')

#識別驗證碼
code_text = transformCode('./code.jpg',1004)


login_url = 'https://so.gushiwen.org/user/login.aspx?from=http%3a%2f%2fso.gushiwen.org%2fuser%2fcollect.aspx'
data = {
    '__VIEWSTATE': '8/BKAQBaZHn7+GP+Kl2Gx43fFO1NI32RMyVae0RyrtFQue3IAhzQKvkml41cIT42Y//OcQccA8AqGYkvB+NFkU43uaHqU69Y0Z1WT3ZRrr4vR+CF7JlBG29POXM=',
    '__VIEWSTATEGENERATOR': 'C93BE1AE',
    'from': 'http://so.gushiwen.org/user/collect.aspx',
    'email': 'www.zhangbowudi@qq.com',
    'pwd': 'bobo328410948',
    'code': code_text,
    'denglu': '登陸',
}
print(code_text)
page_text = requests.post(login_url,headers=headers,data=data).text

with open('./login.html','w',encoding='utf-8') as fp:
    fp.write(page_text)

版本二 :

版本二遇到的問題 :

​ 沒有攜帶cookie ,且這個網站的cookie在驗證碼的請求裏

#驗證碼的識別:將驗證碼下載到本地而後提交給打嗎平臺進行識別
main_url = 'https://so.gushiwen.org/user/login.aspx?from=http://so.gushiwen.org/user/collect.aspx'
page_text = requests.get(main_url,headers=headers).text
tree = etree.HTML(page_text)
code_src = 'https://so.gushiwen.org'+tree.xpath('//*[@id="imgCode"]/@src')[0]
request.urlretrieve(code_src,'./code.jpg')

#解析出動態變化的請求參數
__VIEWSTATE = tree.xpath('//*[@id="__VIEWSTATE"]/@value')[0]
__VIEWSTATEGENERATOR = tree.xpath('//*[@id="__VIEWSTATEGENERATOR"]/@value')[0]

#識別驗證碼
code_text = transformCode('./code.jpg',1004)


login_url = 'https://so.gushiwen.org/user/login.aspx?from=http%3a%2f%2fso.gushiwen.org%2fuser%2fcollect.aspx'
data = {
    '__VIEWSTATE': __VIEWSTATE,
    '__VIEWSTATEGENERATOR': __VIEWSTATEGENERATOR,
    'from': 'http://so.gushiwen.org/user/collect.aspx',
    'email': 'www.zhangbowudi@qq.com',
    'pwd': 'bobo328410948',
    'code': code_text,
    'denglu': '登陸',
}
print(code_text)
page_text = requests.post(login_url,headers=headers,data=data).text

with open('./login.html','w',encoding='utf-8') as fp:
    fp.write(page_text)

版本三 (完美版):

s = requests.Session()
#驗證碼的識別:將驗證碼下載到本地而後提交給打嗎平臺進行識別
main_url = 'https://so.gushiwen.org/user/login.aspx?from=http://so.gushiwen.org/user/collect.aspx'
page_text = s.get(main_url,headers=headers).text
tree = etree.HTML(page_text)
code_src = 'https://so.gushiwen.org'+tree.xpath('//*[@id="imgCode"]/@src')[0]

# request.urlretrieve(code_src,'./code.jpg')
code_data = s.get(code_src,headers=headers).content
with open('./code.jpg','wb') as fp:
    fp.write(code_data)

#解析出動態變化的請求參數
__VIEWSTATE = tree.xpath('//*[@id="__VIEWSTATE"]/@value')[0]
__VIEWSTATEGENERATOR = tree.xpath('//*[@id="__VIEWSTATEGENERATOR"]/@value')[0]

#識別驗證碼
code_text = transformCode('./code.jpg',1004)


login_url = 'https://so.gushiwen.org/user/login.aspx?from=http%3a%2f%2fso.gushiwen.org%2fuser%2fcollect.aspx'
data = {
    '__VIEWSTATE': __VIEWSTATE,
    '__VIEWSTATEGENERATOR': __VIEWSTATEGENERATOR,
    'from': 'http://so.gushiwen.org/user/collect.aspx',
    'email': 'www.zhangbowudi@qq.com',
    'pwd': 'bobo328410948',
    'code': code_text,
    'denglu': '登陸',
}
print(code_text)
page_text = s.post(login_url,headers=headers,data=data).text

with open('./login.html','w',encoding='utf-8') as fp:
    fp.write(page_text)
- 反爬機制
    - robots
    - UA檢測
    - 圖片懶加載 
    - 代理
    - cookie
    - 驗證碼
    - 動態變化的請求參數
    - 動態加載的數據

10. 使用線程池提高爬取數據的效率

# 同步操做

import time

start = time.time()
def request(url):
    print('正在請求',url)
    time.sleep(2)
    print('請求完畢:',url)
urls = [
    'www.1.com',
    'www.b.com',
    'www.3.com'
]

for url in urls:
    request(url)
print('總耗時:',time.time()-start)
# 異步操做

import time
from multiprocessing.dummy import Pool

start = time.time()
pool = Pool(3)
def request(url):
    print('正在請求',url)
    time.sleep(2)
    print('請求完畢:',url)

urls = [
    'www.1.com',
    'www.b.com',
    'www.3.com'
]

pool.map(request,urls)

print('總耗時:',time.time()-start)
# 爬蟲+ 線程池
# server端
from flask import Flask,render_template
from  time import sleep
app = Flask(__name__)

@app.route('/bobo')
def index_bobo():
    sleep(2)
    return render_template('ip.html')

@app.route('/jay')
def index_jay():
    sleep(2)
    return render_template('login.html')
app.run()

# 爬蟲 + 線程池
import time
from multiprocessing.dummy import Pool
import requests
from lxml import etree
start = time.time()
urls = [
    'http://localhost:5000/jay',
    'http://localhost:5000/bobo'
]

def get_request(url):
    page_text = requests.get(url).text
    return page_text


def parse(page_text):
    tree = etree.HTML(page_text)
    print(tree.xpath('//div[1]//text()'))


pool = Pool(2)
page_text_list = pool.map(get_request,urls)


pool.map(parse,page_text_list)


print(len(page_text_list))


print('總耗時:',time.time()-start)
相關文章
相關標籤/搜索