30.爬蟲總結

http://www.javashuo.com/article/p-uqppchod-gd.htmljavascript

快捷鍵:

"""
插入cell: a b
刪除: x
執行:shift+enter
tab:
cell模式切換:y(m->code) m(code=>m)
shift+tab:打開官方文檔
"""
import numpy as np
np.linspace()  # shift+tab:打開官方文檔

安裝anaconda:

https://www.jianshu.com/p/836a79d1f8a4  # 安裝anaconda

jupyter使用:

https://www.jianshu.com/p/87ed39c4e01b  # 使用
https://zhuanlan.zhihu.com/p/34337292   # 常見問題
https://blog.csdn.net/Data_Arrow/article/details/89188256  # 修改文件目錄,以及修改默認瀏覽器
https://blog.csdn.net/Data_Arrow/article/details/88381388  # 修改文件目錄,以及修改默認瀏覽器

爬蟲簡介:

爬蟲:經過編寫程序,模擬瀏覽器上網,而後讓其去互聯網上爬取數據的過程
爬蟲的分類:
	通用爬蟲:
	聚焦爬蟲:
	增量式:
反爬機制:
反爬策略:
robots.txt協議:聽從或者不聽從

抓包工具:fiddlerphp


無參請求:

1.指定url
2.發起請求
3.獲取相應內容
4.保存數據
import requests
# 1
url = "https://www.sogou.com"
# 2
response = requests.get(url=url)
# 3
page_text = response.text
# 4
with open("sogou.html", "w", encoding="utf-8") as f:
    f.write(page_text)

帶有參數的請求

import requests
url = "https://www.sogou.com/web"
# 封裝參數
wd = input("enter a word:")
params = {
    "query": wd
}
headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.87 Safari/537.36"
}
response = requests.get(url=url, params=params, headers=headers)

# page_text = response.text  # 字符串
page_text = response.content  # 二進制

filename = wd + ".html"
with open(filename, "wb") as f:
    f.write(page_text)

post請求:

import requests
url = "https://fanyi.baidu.com/sug"

# 封裝參數
wd = input("enter a word:")
date = {
    "kw": wd
}
headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.87 Safari/537.36"
}
response = requests.post(url=url, data=date, headers=headers)
page_text = response.text  # 字符串
# page_text = response.content  # 二進制
# page_text = response.json  # 返回對象,若是沒有結果會報錯
print(response.json())

js請求爬取:

js請求優勢難度,由於由於是異步的因此你須要本身去找數據刷新的接口,同時下一個頁面一定使用了上一個頁面中的參數,不然根本沒辦法聯繫起來,因此就須要你本身去找規律。
import requests
url = "http://125.35.6.84:81/xk/itownet/portalAction.do?method=getXkzsList"
headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.87 Safari/537.36"
}

# 封裝參數
# wd = input("enter a word:")
id_list = []
for page in range(1, 2):
    date = {
        "on": "true",
        "page": "page",
        "pageSize": "15",
        "productName": "",
        "conditionType": "1",
        "applyname": "",
        "applysn": "",
    }
    response = requests.post(url=url, data=date, headers=headers)

    # page_text = response.text  # 字符串
    # page_text = response.content  # 二進制
    # page_text = response.json()  # 對象,若是沒有結果會報錯
    page_text = response.json()
    for dic in page_text["list"]:
        id = dic["ID"]
        id_list.append(id)

detail_url = "http://125.35.6.84:81/xk/itownet/portalAction.do?method=getXkzsById"
for id in id_list:

    date = {
        "id": str(id)
    }
    data = requests.post(url=detail_url, data=date, headers=headers).json()
    print(data)

爬取圖片:

import requests
url = "https://up.ruyile.com/jy_img/11/234/151448ie4.jpg"
response = requests.get(url=url, headers=headers)
with open("校花.jpg", "wb") as f:
    f.write(response.content)
# 
import urllib
url = "https://up.ruyile.com/jy_img/11/234/151448ie4.jpg"
urllib.request.urlretrieve(url=url, filename="123.jpg")

re.M和re.S:

import re
string = """fall in love with you
i love you very mush
i love she
i love her"""
print(re.findall("^i.*", string, re.M))  # 對字符串進行多行匹配

string1 = """<div>細思極恐,
你的隊友在看書,
你的敵人在磨刀,
你的閨蜜在減肥,
隔壁老王在睡覺。</div>"""
print(re.findall("^.*", string1, re.S))  # 將整個字符串做爲一行

正則匹配:

import re
import requests
# url = "http://125.35.6.84:81/xk/itownet/portalAction.do?method=getXkzsList"
url = "https://www.neihanba.com/pic/"
headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.87 Safari/537.36"
}
response = requests.get(url=url, headers=headers)

# 其中<li class="piclist[23]">.*?</li>匹配li標籤中的全部,以後再加上<img src="(.*?)" alt=.*?前面過濾出來的內容進行二次匹配src中的內容。
image_list = re.findall('<li class="piclist[23]">.*?<img src="(.*?)" alt=.*?</li>', response.text, re.S)

print(image_list)
for url in image_list:
    print(url)

"""
<li class="piclist2">
  <a href="/pic/1175728.html" target="_blank" class="imgbox">
  <img src="https://file.neihanba.com/file/2020/02/08/smalla37b627df679038d444105686bd6c4f4.jpg"     alt="這妹子自帶安全氣囊" width="100" height="100"></a>
  <h4> <a target="_blank" href="/pic/1175728.html">這妹子自帶安全氣囊</a><span class="new">new</span></h4>
  <p class="g9">  </p>
  <p class="ft"> <span class="r">2020-02-08</span> <span class="view" title="瀏覽次數"><i></i><em>0</em></span> <span class="good" title="好評"><i></i><em>0</em></span> <span class="bad" title="差評"><i></i><em>0</em></span> </p>
</li>
"""

bs4:

http://www.javashuo.com/article/p-eupvvyhc-ed.html bs4以及xpath使用html

pip install bs4
pip install lxml
解析原理:
	1.即將要進行解析的原碼加載到bs對象
    2.調用bs對象中的相關方法或屬性進行源碼中的相關標籤的定位
    3.將定位到的標籤之間存在的文本或者屬性值獲取
- 須要將pip源設置爲國內源,阿里源、豆瓣源、網易源等
   - windows
    (1)打開文件資源管理器(文件夾地址欄中)
    (2)地址欄上面輸入 %appdata%
    (3)在這裏面新建一個文件夾  pip
    (4)在pip文件夾裏面新建一個文件叫作  pip.ini ,內容寫以下便可
        [global]
        timeout = 6000
        index-url = https://mirrors.aliyun.com/pypi/simple/
        trusted-host = mirrors.aliyun.com
   - linux
    (1)cd ~
    (2)mkdir ~/.pip
    (3)vi ~/.pip/pip.conf
    (4)編輯內容,和windows如出一轍
- 須要安裝:pip install bs4
     bs4在使用時候須要一個第三方庫,把這個庫也安裝一下
     pip install lxml
基礎使用
----------------------------------------------------------------------------------------
使用流程:       
    - 導包:from bs4 import BeautifulSoup
    - 使用方式:能夠將一個html文檔,轉化爲BeautifulSoup對象,而後經過對象的方法或者屬性去查找指定的節點內容
        (1)轉化本地文件:
             - soup = BeautifulSoup(open('本地文件'), 'lxml')
        (2)轉化網絡文件:
             - soup = BeautifulSoup('字符串類型或者字節類型', 'lxml')
        (3)打印soup對象顯示內容爲html文件中的內容

基礎鞏固:
    (1)根據標籤名查找
        - soup.a   只能找到第一個符合要求的標籤
    (2)獲取屬性
        - soup.a.attrs  獲取a全部的屬性和屬性值,返回一個字典
        - soup.a.attrs['href']   獲取href屬性
        - soup.a['href']   也可簡寫爲這種形式
    (3)獲取內容
        - soup.a.string  # 這個是獲取單個內容
        - soup.a.text # 獲取標籤中的全部內容,嵌套一樣獲取,返回列表
        - soup.a.get_text() # 獲取標籤中的全部內容,嵌套一樣獲取,返回列表
       【注意】若是標籤還有標籤,那麼string獲取到的結果爲None,而其它兩個,能夠獲取文本內容
    (4)find:找到第一個符合要求的標籤
        - soup.find('a')  找到第一個符合要求的
        - soup.find('a', title="xxx")
        - soup.find('a', alt="xxx")
        - soup.find('a', class_="xxx")
        - soup.find('a', id="xxx")
    (5)find_all:找到全部符合要求的標籤
        - soup.find_all('a')
        - soup.find_all(['a','b']) 找到全部的a和b標籤
        - soup.find_all('a', limit=2)  限制前兩個
    (6)根據選擇器選擇指定的內容
               select:soup.select('#feng')
        - 常見的選擇器:標籤選擇器(a)、類選擇器(.)、id選擇器(#)、層級選擇器
            - 層級選擇器:
                div .dudu #lala .meme .xixi  下面好多級
                div > p > a > .lala          只能是下面一級
        【注意】select選擇器返回永遠是列表,須要經過下標提取指定的對象

bs4使用:java

from bs4 import BeautifulSoup
import requests


url = "http://www.shicimingju.com/book/sanguoyanyi.html"

headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.87 Safari/537.36"
}

response = requests.get(url=url, headers=headers)
page_text = response.text
soup = BeautifulSoup(page_text, "lxml")

a_list = soup.select(".book-mulu > ul > li > a")  # 返回的是a標籤
f = open("sangu.txt", "w", encoding="utf-8")
for a in a_list:
    title = a.string
    detail_url = "http://www.shicimingju.com" + a["href"]
    page_text2 = requests.get(url=detail_url, headers=headers).text
    soup2 = BeautifulSoup(page_text2, "lxml")
    content = soup2.find("div", class_="chapter_content").text
    f.write(title + "\n" + content)
    print(title, "over")
print("over")
f.close()

xpath使用:

環境安裝:pip install lxml
解析原理:
	獲取頁面源碼數據
    實例化一個etree的對象,而且加你頁面源碼加載到該對象中
    調用到該對象的xpath方法進行指定標籤的定位
    注意:xpath函數必須結合着xpath表達式進行標籤訂位和內容捕獲
--------------------------------------------------------------------------------
# 經常使用xpath表達式回顧
屬性定位:
    #找到class屬性值爲song的div標籤
    //div[@class="song"] 
層級&索引定位:
    #找到class屬性值爲tang的div的直系子標籤ul下的第二個子標籤li下的直系子標籤a
    //div[@class="tang"]/ul/li[2]/a
邏輯運算:
    #找到href屬性值爲空且class屬性值爲du的a標籤
    //a[@href="" and/or @class="du"]
模糊匹配:
    //div[contains(@class, "ng")]
    //div[starts-with(@class, "ta")]
取文本:
    # /表示獲取某個標籤下的文本內容
    # //表示獲取某個標籤下的文本內容和全部子標籤下的文本內容
    //div[@class="song"]/p[1]/text()
    //div[@class="tang"]//text()
取屬性:
    //div[@class="tang"]//li[2]/a/@href
代碼中使用xpath表達式進行數據解析:
1.下載:pip install lxml
2.導包:from lxml import etree
import requests
from lxml import etree

url = "https://bj.58.com/ershoufang/"

headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.87 Safari/537.36"
}

response = requests.get(url=url, headers=headers)
page_text = response.text

tree = etree.HTML(page_text)
li_list = tree.xpath('//ul[@class="house-list-wrap"]/li')
for li in li_list:
    text = li.xpath('./div[2]/h2/a/text()')[0]
    print(text)
    price = li.xpath('./div[3]//text()')
    print("".join(price))

中文亂碼問題:

案例一:中文亂碼node

# 解決爬蟲中文問題
# 1 對整個返回的結果進行從新的編碼
response = requests.get(url=url, headers=headers)
response.encoding = 'utf-8'
page_text = response.text
# 上面有時候不能解決編碼的問題,使用局部解決辦法
# 2 對須要的文字進行從新編碼
title = li.xpath('./a/h3/text()')[0].encode('iso-8859-1').decode('gbk')
# 3 所有從新編碼
response = requests.get(url=url).text.encode('iso-8859-1').decode('utf-8')
import requests
from lxml import etree

url = 'https://www.xxx.com.cn'

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'
}

page_text = requests.get(url=url, headers=headers).text


tree = etree.HTML(page_text)

li_list = tree.xpath('//div[@id="auto-channel-lazyload-article"]/ul/li')
for li in li_list:
    try:
        title = li.xpath('./a/h3/text()')[0].encode('iso-8859-1').decode('gbk')
        a_url = f"https:{li.xpath('./a/@href')[0]}"
        img_src = f"https:{li.xpath('./a/div/img/@src')[0]}"
        desc = li.xpath('./a/p/text()')[0].encode('iso-8859-1').decode('gbk')
    except IndexError:
        continue
    print(title)
    print(a_url)
    print(img_src)
    print(desc)

案例二:中文亂碼python

import os
import urllib
import requests
from lxml import etree

url = 'http://pic.netbian.com/4kmeinv'
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, headers=headers)
# 1.方式一
# response.encoding = "utf-8"
page_text = response.text

tree = etree.HTML(page_text)
li_list = tree.xpath("//div[@class='slist']/ul/li")
if not os.path.exists("meinv"):
    os.mkdir("meinv")
for li in li_list:
    img_name = li.xpath("./a/b/text()")[0]
    # 2.方式二
    img_name = img_name.encode("ISO-8859-1").decode("gbk")
    img_url = "http://pic.netbian.com" + li.xpath("./a/img/@src")[0]
    filename = "./meinv/" + img_name + ".jpg"
    urllib.request.urlretrieve(url=img_url, filename=filename)
    print("ok", img_url)
print("over")

案例三:加密圖片mysql

import base64
import requests
from lxml import etree

url = 'http://jiandan.net/ooxx'
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, headers=headers)
# 1.方式一
# response.encoding = "utf-8"
page_text = response.text

tree = etree.HTML(page_text)
span_list = tree.xpath('//span[@class="img-hash"]/text()')
for im in span_list:
    # 這個網站之前的圖片的是通過bs64加密的因此須要使用
    img = "http://" + base64.b64decode(im).decode()
    print(img)

案例四:簡歷爬取linux

import os

import requests
from lxml import etree
import urllib
import random

url = "http://sc.chinaz.com/jianli"


headers = {
    # "Connection": "close",  # 防止由於請求過快,而致使ip被封,相應以後直接斷開
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.87 Safari/537.36"
}

if not os.path.exists("jianli"):
    os.mkdir("jianli")

response = requests.get(url=url, headers=headers)
response.encoding = "utf-8"
page_text = response.text

tree = etree.HTML(page_text)

li_list = tree.xpath('//div[@id="container"]/div')
for li in li_list:
    print("1")
    detail_url = li.xpath("./a/@href")[0]
    name = li.xpath("./a/img/@alt")[0]
    detail_page = requests.get(url=detail_url, headers=headers).text
    print("2")
    tree2 = etree.HTML(detail_page)
    down_list = tree2.xpath("//div[@class='clearfix mt20 downlist']/ul/li/a/@href")
    down_url = random.choice(down_list)
    print("3")
    down_page = requests.get(url=down_url, headers=headers).content  # 圖片,文件,使用content直接拿取內容
    print("4")
    file_path = "jianli/" + name + ".rar"
    with open(file_path, "wb") as f:
        f.write(down_page)
        print(name, "下載完成")
print("over")

案例五:城市信息web

url = "https://www.aqistudy.cn/"

懶加載:

當某些資源出如今屏幕的某些高度的時候,才加載資源,能夠加少服務器壓力,優化頁面,提升用戶體驗。

代理:

www.goubanjia.com 快代理 西刺代理

驗證碼-雲打碼

# 普通用戶和開發者用戶都要註冊
- 登陸普通用戶(查看餘額)
- 登陸開發者用戶
	- 建立個人軟件
    - 下載雲打碼的python3http事例代碼
  
import requests
from lxml import etree
import urllib

session = requests.Session()  # 獲取請求生成的session

headers = {
    # "Connection": "close",  # 防止由於請求過快,而致使ip被封,相應以後直接斷開
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.87 Safari/537.36"
}

url = "http://www.renren.com"
page_text = session.get(url=url, headers=headers)

tree = etree.HTML(page_text)
conde_img_url = tree.xpath("//*[id='verifyPic_login']/@src")
# 這裏獲取到圖片以後須要使用雲打碼平臺提取驗證碼並返回
urllib.request.urlretrieve(url=conde_img_url, filename="code.jpg")

# 登陸
url = "http://www.renren.com/289676607/profile"
# data中存放的就是用戶fillder抓取的登陸時的全部參數,將對應的參數進行補充就能夠發post請求登陸了
data = {}
session.post(url=url, data=data, headers=headers)

# 獲取登錄以後頁面的數據,這裏的session中已經將cookie的值自動保存了
page_text = session.get(url=url, headers=headers).text

selenium:

- pip install selenium
- 編碼流程
	- 導報:from selenium import webdirver
    - 實例化某一個瀏覽器對象
    - 自制定自動化操做
import time
from selenium import webdirver
# 這裏面須要去下載谷歌的驅動,其中的path就是你下載的谷歌驅動存放路徑
# http://chromedriver.storage.googleapis.com/index.html下載地址
brower = webdirver.Chorme(executable_path=path)
text_input = brower.find_element_by_id("kw")
text_input.send_keys("人民幣")
brower.find_element_by_id("su").click()
time.sleep(2)
# 獲取頁面源碼數據
page_text = brower.page_source 
brower.quit()
import time
# 無頭設置
# 方式一
from selenium.webdriver.chrome.options import Options
chrom_options = Options()
chrom_options.add_argument("--headless")
chrom_options.add_argument("--disable-gpu")
brower= Chrome(chrom_options=chrom_options)
# 方式二:使用PhantomJS,可是他已經中止更新了,直接使用PhantomJS的.exe文件替換executable_path中的使用的谷歌驅動就好了

# 規避檢測
from selenium.webdriver import ChromeOptions
option = ChromeOptions()
option.add_experimental_option('excludeSwitches', ['enable-automation'])
driver = Chrome(options=option)

from selenium import webdriver
url = "https://movie.douban.com/explore#!type=movie&tag=%E5%86%B7%E9%97%A8%E4%BD%B3%E7%89%87&sort=recommend&page_limit=20&page_start=0"
brower = webdriver.Chrome(executable_path=r"C:\Users\lzh\Downloads\chromedriver.exe")

brower.get(url)
time.sleep(3)
brower.execute_script("window.scrollTo(0, document.body.scrollHeight)")
time.sleep(3)
brower.execute_script("window.scrollTo(0, document.body.scrollHeight)")
time.sleep(3)
brower.execute_script("window.scrollTo(0, document.body.scrollHeight)")

page_text = brower.page_source
with open("./douban.html", "w", encoding="utf-8") as fp:
    fp.write(page_text)
time.sleep(1)
brower.quit()

iframe標籤:ajax

import time
from selenium import webdriver
url = "https://qzone.qq.com/"
brower = webdriver.Chrome(executable_path=r"C:\Users\lzh\Downloads\chromedriver.exe")

brower.get(url)
# 切換到iframe標籤中
brower.switch_to.frame("login_frame")
click_btn = brower.find_element_by_id("switcher_plogin").click()
text_input = brower.find_element_by_id("u")
text_input.send_keys("3164626382")
text_input = brower.find_element_by_id("p")
text_input.send_keys("lzh19950326")
click_but = brower.find_element_by_id("login_button").click()
time.sleep(2)
# 獲取頁面源碼數據
page_text = brower.page_source  # 可使用tree對頁面源碼進行解析
brower.quit()

線程池爬取文件:

import requests
import re
from lxml import etree
from multiprocessing.dummy import Pool
import random
pool = Pool(5)

url = "https://www.pearvideo.com/category_1"
headers = {
    # "Connection": "close",  # 防止由於請求過快,而致使ip被封,相應以後直接斷開
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.87 Safari/537.36"
}

response = requests.get(url, headers).text
tree = etree.HTML(response)
li_list = tree.xpath("//div[@id='listvideoList']/ul/li")
video_url_list = []
for li in li_list:
    detail_url = "https://www.pearvideo.com/" + li.xpath("./div/a/@href")[0]
    detail_text = requests.get(url=detail_url, headers=headers).text
    video_url = re.findall('srcUrl="(.*?)",vdoUrl', detail_text, re.S)[0]
    video_url_list.append(video_url)

def getVideoData(url):
    content = requests.get(url, headers=headers).content
    return content

view_data_list = pool.map(getVideoData, video_url_list)

def seveVideoData(data):
    filename = str(random.randint(0, 100)) + ".mp4"
    with open(filename, "wb") as f:
        f.write(data)

pool.map(seveVideoData, view_data_list)

移動端爬取:

# 回顧
- 開發者用戶:
	- 建立一個軟件
    - 下載示例代碼
    - 超時時間
- 模擬登錄:
	- 爬取一些基於當前用戶的相關數據信息
- 驗證碼
- cookie
- 動態數據加載
- selenium phantomjs 谷歌無頭瀏覽器 驅動程序 
- 做用:爬取一些動態加載的頁面數據
# 移動端數據加載
- 配置fiddler
	- tools -> options -> connection -> allow remote computer to connect
    - fiddler prot : xxxx
- 移動端安裝fiddler的證書
	- 保證移動端和fiddler所在的pc的網絡是在同一個網段下
    - 在移動端瀏覽器中:fiddler所在機器的ip地址:fiddler的端口號
    - 證書下載完畢以後進行安裝且設置信任
- 配置手機的網絡
	- 給手機設置一個代理ip:port

空氣質量案例爬取(****):

# 分析
# 1改變頁面中的查詢條件,而後點擊插敘按鈕,經過抓包工具捕獲相關的數據包,最終定位到數據包的請求url
# 2該數據包中發現:post請求攜帶了一個動態變化且加密的請求參數,d,而且請求到的數據也是一組密文數據
# 3發現點擊了查詢按鈕以後發送了一個ajax請求,該請求將請求參數加密以後發起了post請求
# 4經過火狐定位到搜素按鈕綁定的click點擊事件(getData)
# 5剖析getData函數的實現:
    # type=HOUR
    # getAOIData() ,getWeatherData()
#6.# getAOIData() ,getWeatherData()中的內容基本一致
    # getServerData
    # param的字典:有4個鍵值
    # 調用了getServerData(method, param, 匿名函數,0.5)
#7.剖析getServerdata
    # 在谷歌抓包工具中,定位到etServerdata的實現,發現函數被加密了,js混淆
    # js反混淆
    # 解密網址:http://www.bm8.com.cn/jsConfusion/

#8 getServer反混淆以後的代碼
    # 發現getParam函數,發現加密以後的請求參數
    # decodeData(data):時間加密的響應數據進行解密的

# 10 須要經過python調用js的相關代碼:PyexecJs
    # 可讓python對js代碼進行模擬運行
    # pip install PyExecJS
    # 安裝node.js環境

# 11將反混淆以後的呢一段js代碼放入一個js文件中
    # d懂事編寫以下的js函數拿到加密以後的請求參數
    # function getPostParamCode(method, city, type, startTime, endTime){
    #     var param = {};
    #     param.city = city;
    #     param.type = type;
    #     param.startTime = startTime;
    #     param.endTime = endTime;
    #     return getParam(method, param);
    # }

# 12接下來就是使用
    # 經過 PyExecJS編譯那一個js文件,以後使用execj經過python調用js
import requests
import execjs
node = execjs.get()

# Params
method = 'GETCITYWEATHER'
city = '北京'
type = 'HOUR'
start_time = '2018-01-25 00:00:00'
end_time = '2018-01-25 23:00:00'

# Compile javascript
file = 'jsCode.js'
ctx = node.compile(open(file).read())

# Get params
js = 'getPostParamCode("{0}", "{1}", "{2}", "{3}", "{4}")'.format(method, city, type, start_time, end_time)
params = ctx.eval(js)

# 發起post請求
url = 'https://www.aqistudy.cn/apinew/aqistudyapi.php'
response_text = requests.post(url, data={'d': params}).text

# 對加密的響應數據進行解密
js = 'decodeData("{0}")'.format(response_text)
decrypted_data = ctx.eval(js)
print(decrypted_data)

"""
動態變化的請求參數
js加密
js混淆
"""

scripy框架:

https://www.bilibili.com/video/av84457418?p=602

# 安裝    
    # Linux:
          pip3 install scrapy
    # Windows:
          a. pip3 install wheel
          b. 下載twisted http://www.lfd.uci.edu/~gohlke/pythonlibs/ #twisted
          c. 進入下載目錄,執行 pip3 install Twisted‑17.1.0‑cp35‑cp35m‑win_amd64.whl
          d. pip3 install pywin32
          e. pip3 install scrapy
# Scrapy異步框架
	-pySpider
# 什麼是框架?
	- 就是一個就有很強通用性且集成了不少功能的項目模板(能夠被引用在各類需求中)
# scrapy集成好的功能
	- 高性能的數據解析操做(xpath)
    - 高性能的數據下載
    - 高新能的持久化存儲
    - 中間件
    - 全棧數據爬取數據
    - 分佈式,redis
    - 請求傳參的機制(深度爬取)
    - scrapy中合理的應用selenium
# 建立工程
	- scrapy startproject ProName
    - cd ProName
    - scrapy genspider spiderName www.xxx.com  : 建立爬蟲文件
    - scrapy crawl sipderName  # 執行
    - settings.py文件中對下面兩個參數進行配置
    	- USER_AGENT = ""
	    - ROBOTSTXT_OBEY = False
        # 若是不設置日誌等級,scrapy crawl sipderName會輸出一大堆日誌,可是其中不少日誌信息是沒用
        - LOG_LEVLE = 'ERROR' 指定日誌登記
        - LOG_FILE = "./data/log.log"  # 指定log文件的文件目錄
# scrapy數據解析
	-extract() : 列表是有多個元素
    -extract_first(): 單個元素
# scrapy持久化存儲
	- 基於終端指令:
    	- 只能夠將parse方法中的返回值存儲到磁盤文件中
        - scrapy crawl first -o fileName.csv  # 後綴必須是.csv.json.lxml
--------------------------------------基於終端指令存儲文件代碼---------------------------------------
import scrapy
class FirstSpider(scrapy.Spider):
    # 爬蟲文件名名稱,爬蟲源文件的惟一標識
    name = 'first'
    # 容許的域名:下載url列表中的域名
    # allowed_domains = ['www.xxx.com']
    # 其實的url列表:列表中的立標元素會被scrapy自動的進行請求發送
    start_urls = ['https://dig.chouti.com/']
    # 解析數據
    def parse(self, response):
        data_list =[]
        div_list = response.xpath("/html/body/main/div/div/div[1]/div/div[2]/div[1]")
        for div in div_list:
            # 注意:xpath返回的列表中的元素是Selector對象,咱們要解析獲取到的字符串數據是存儲在該對象中的
            # 必須進過一個extract()的操做才能夠將該對象中存儲的字符串數據獲取
            # .extract()返回的是一個列表 .extract_first()返回的是單個值
            # content = div.xpath("./div/div/div[1]/a/text()")[0].extract()
            content = div.xpath("./div/div/div[1]/a/text()").extract_first()
            # xpath返回的列表中的列表元素有多個(selector對象),想要將每個列表元素多對應的Selector中的
            # 字符串提取出來該如何操做?response.xpath(/div//text()).extract()
            print(content)  # selector xxx="fasdf" data="fasdfds"
            data_list.append(content)
        return data_list
    -------------------------------------------結束-------------------------------------------
    - 基於管道:pipelines.py
    	- 編碼流程
        	- 1.數據解析
            - 2.在item的類中定義相關的屬性名稱,item.py中
                import scrapy
                class FirstBloodItem(scrapy.Item):
                    content = scrapy.Field()  # filed是一個萬能的數據類型
            - 3.將解析的數據存儲封裝到item類型的對象中,item["content"], spiderName.py中
                content = div.xpath("./div/div/div[1]/a/text()").extract_first()
                item = FirstBloodItem()
                item["content"] = content
            - 4.將item兌現提交給管道  ,spiderName.py中
            	yield item
            - 5.在管道類中process_item負責接收item對象,對item數據進行任意持久化存儲(piplines.py)
            	# 專門用做於持久化存儲
                class FirstBloodPipeline(object):
                    fp = None
                    def open_spider(self, spider):
                        print("只會在爬蟲開始的時候執行一次")
                        self.fp = open("./data.txt", "w", encoding="utf-8")
                    def process_item(self, item, spider):
                        content = item["content"]
                        self.fp.write(content)
                        return item
                    def close_spider(self, spider):
                        print("只會在爬蟲結束的時候執行一次")
                        self.fp.close()
            - 6.在配置文件settings.py中開啓管道:ITEM_PIPELINES參數
            	ITEM_PIPELINES = {
   'first_blood.pipelines.FirstBloodPipeline': 300,  # 300表示的是優先級,這個值越小,優先級越高
}
            - 補充:
            	- 管道文件中的一個管道類表示將數據存儲到某一種形式的平臺
                - 若是管道文件中定義了多個管道類,爬蟲類提交的item會給到優先級最高的管道類
                - process_item方法的實現中的return item的操做表示將item傳遞給下一個即將被執行的管道類
   ----------------------------------------基於管道-----------------------------------
    # 管道的持久化存儲
    def parse(self, response):
        div_list = response.xpath("/html/body/main/div/div/div[1]/div/div[2]/div[1]/div")
        for div in div_list:
            # 注意:xpath返回的列表中的元素是Selector對象,咱們要解析獲取到的字符串數據是存儲在該對象中的
            # 必須進過一個extract()的操做才能夠將該對象中存儲的字符串數據獲取
            # .extract()返回的是一個列表 .extract_first()返回的是單個值
            # content = div.xpath("./div/div/div[1]/a/text()")[0].extract()
            content = div.xpath("./div/div/div/div[1]/a/text()").extract_first()
            item = FirstBloodItem()
            item["content"] = content
            # xpath返回的列表中的列表元素有多個(selector對象),想要將每個列表元素多對應的Selector中的
            # 字符串提取出來該如何操做?response.xpath(/div//text()).extract()
            print(content, "*" * 50)  # selector xxx="fasdf" data="fasdfds"
            yield item  # 經過yield關鍵字將數據傳輸到pipline中
   -------------------------------------------------------------------------

多方式存儲:

settings.py
ITEM_PIPELINES = {
   # 'huyapro.pipelines.HuyaproPipeline': 300,
   # 'huyapro.pipelines.mysqlPipeLine': 301,
   'huyapro.pipelines.redisPipeLine': 302,
}
piplines.py
import pymysql
from redis import Redis


class HuyaproPipeline(object):
    fp = None

    def open_spider(self, spider):
        self.fp = open("huya.txt", "w", encoding="utf-8")
        print("open file")

    def process_item(self, item, spider):  # item就是接收到的爬蟲類提交過來的item對象
        self.fp.write(item["title"] + ":" + item["author"] + ":" + item["orders"] + "\n")
        print("寫入成功")
        return item

    def close_spider(self, spider):
        self.fp.close()
        print("close file")


class mysqlPipeLine(object):

    def open_spider(self, spider):
        self.conn = pymysql.Connection(
            host="127.0.0.1", user="root", password="1",
            database="spider", port=3306)
        print(self.conn)

    def process_item(self, item, spider):
        sql = "insert into huya values('{}','{}','{}')".format(item["title"], item["author"], item["orders"])
        self.cursor = self.conn.cursor()
        try:
            self.cursor.execute(sql)
            self.conn.commit()
        except Exception as e:
            print(e)

    def close_spider(self, spider):
        self.cursor.close()
        self.conn.close()
        print("close")


class redisPipeLine(object):
    conn = None

    def open_spider(self, spider):
        self.conn = Redis(host="192.168.31.199", port=6379)
        print(self.conn)

    def process_item(self, item, spider):
        self.conn.lpush("huyaList", item)
        return item

全棧爬取:

# 管道的持久化存儲
	- 數據解析(爬蟲類)
    - 講解洗出來的數據封裝到item類的對象中(爬蟲類)
    - 將爬取到的數據提價給管道(爬蟲類)
    - 在管道類中的process_item中接收item對象並進行任意形式的持久化存儲(管道類)
    - 在配置文件中開啓管道類
    - 細節:
    	- 將爬取到的數據進行備份?
        	- 一個管道類對應一個平臺的持久化存儲
        - 有多個管道類是否意味着多個管道類均可以接受到爬蟲文件提交的item?
        	- 只有優先級最高的管道才能夠接收到item,剩下管道類是須要從優先級最高的管道類接收參數
# 基於Spider父類進行全棧數據爬取:
	- 全站數據的爬取:將全部頁碼對應的頁面上數據進行解析
    - 手動請求的發送:
    	- yield scrapy.Request(url, callback)
    - 對yield總結:
    	- 向管道提交item的時候:yeild item
        - 手動請求發送:yield scrapy.Request(url, callback)
    - 手動發送post請求:
    	yield scrapy.FormRequest(url, formdata, callback) # formdata是一個字典形式的請求參數
--------------------------------------------全棧數據爬取--------------------------------------------
import scrapy
from huyaall.items import HuyaallItem
class HuyaSpider(scrapy.Spider):
    name = 'huya'
    # allowed_domains = ['www.xxx.com']
    start_urls = ['https://www.huya.com/g/xingxiu']
    # 通用的url模板
    url = "https://www.huya.com/cache.php?m=LiveList&do=getLiveListByPage&gameId=1663&tagAll=0&page=%d"

    def parse(self, response):
        li_list = response.xpath('//*[@id="js-live-list"]/li')
        for li in li_list:
            title = li.xpath("./a[2]/text()").extract_first()
            author = li.xpath("./span/span[1]/i/text()").extract_first()
            orders = li.xpath("./span/span[2]/i[2]/text()").extract_first()

            # 實例化item類類型的對象
            item = HuyaallItem()
            item["title"] = title
            item["author"] = author
            item["orders"] = orders
            yield item

        # 手動請求的發送
        for i in (2, 5):
            new_url = format(self.url % i)
            # 發起的是get請求
            yield scrapy.Request(url=new_url, callback=self.parse_other)

    # 全部的解析方法必須根據parse進行定義,必需要有parse一樣的參數
    def parse_other(self, response):
        print(response.text)

1584246487476

# scrapy五大核心組件:
上圖就是scrapy五大核心組件
1.spider獲取到url傳輸給引擎
2.引擎將url傳輸給調度器
3.調度器經過過濾器對重複的url進行過濾,並將過濾好的url放入隊列中
4.引擎從隊列中拿去url返回給下載器,
5.下載器到互聯網上請求數據
6.返回數據給下載器response
7.下載將response交給引擎,引擎再將數據交給spider
8.spider拿到數據以後對數據進行解析xpath等
9.spider將數據交給引擎,引擎將數據傳輸給管道
#scrapy的請求參數:
	- 做用: 實現深度爬取
	- 使用場景: 爬去的數據不在同一個頁面中
	- 傳遞item: yield scrapy.Request(detail_url, callback=self.parse_detail, meta={"item": item})
    - 接收item: respone.meta

http://www.javashuo.com/article/p-tpzlcfow-cv.html

# 提高scrapy爬取數據的效率
	- 在配置文件中進行相關設置
    # 增長併發:
    默認scrapy開啓的併發線程爲32個,能夠適當進行增長。在settings配置文件中修改CONCURRENT_REQUESTS = 100值爲100,併發設置成了爲100。

	# 下降日誌級別:
    在運行scrapy時,會有大量日誌信息的輸出,爲了減小CPU的使用率。能夠設置log輸出信息爲INFO或者ERROR便可。在配置文件中編寫:LOG_LEVEL = ‘INFO’

	# 禁止cookie:
    若是不是真的須要cookie,則在scrapy爬取數據時能夠進制cookie從而減小CPU的使用率,提高爬取效率。在配置文件中編寫:COOKIES_ENABLED = False

	# 禁止重試:
    對失敗的HTTP進行從新請求(重試)會減慢爬取速度,所以能夠禁止重試。在配置文件中編寫:RETRY_ENABLED = False

	# 減小下載超時:
    若是對一個很是慢的連接進行爬取,減小下載超時能夠能讓卡住的連接快速被放棄,從而提高效率。在配置文件中進行編寫:DOWNLOAD_TIMEOUT = 10 超時時間爲10s
# scrapy的中間件:
	- 爬蟲中間件
    - 下載中間件(重點):處於引擎和下載器之間
    	- 做用:批量攔截全部的請求和響應
        - 問什麼攔截請求
        	- 篡改請求的頭信息(UA假裝)
            - 修改請求對應的ip(代理)
        - 問什麼攔截響應
        	- 篡改響應數據, 篡改響應對象

selenium在scrapy中的使用流程:

- 爬取網易新聞的新聞標題和內容
- selenium在scrapy中的使用流程
	- 在爬蟲類中定義一個bro的屬性,就是實例化的瀏覽器對象
    - 在爬蟲類中重寫父類的一個closed(self,spider),在方法中關閉bro
    - 在中間中進行瀏覽器自動化的操做
# 做業
- 網易新聞
- http://sc.chinaz.com/tupian/xingganmeinvtupian.html

selenium在scrapy代碼:

# midddlerwares.py
import time

from scrapy.http import HtmlResponse


class WangyiproDownloaderMiddleware(object):

    def process_response(self, request, response, spider):
        """
        參數:
        :param request:   攔截到的請求對應的響應對象
        :param response:  攔截到全部的響應對象(1+5+n)
        :param spider:  爬蟲類實例化的對象,能夠實現爬蟲類和中間類的數據交互
        :return:
        # 攔截五個板塊的響應對象,將其替換成五個符合需求的新的相應對象進行返回
        """
        if request.url in spider.model_list:
            # url:相應對象的請求對象的url
            # body:響應數據, 可使用selenium請求拿出page_source
            bro = spider.bro
            bro.get(request.url)
            time.sleep(3)
            page_text = bro.page_source
            new_response = HtmlResponse(url=request.url, body=page_text, encoding="utf-8", request=request)
            return new_response
        else:
            return response
-------------------------------------------------------------------------
# piplines.py
import pymysql


class WangyiproPipeline(object):
    conn = None
    cursor = None

    def open_spider(self, spider):
        self.conn = pymysql.Connection(host="127.0.0.1", user="root", password="1",
                                       database="spider", port=3306)
        print(self.conn)

    def process_item(self, item, spider):
        print("進入process_item函數")
        sql = "insert into wangyi values ('{}', '{}')".format(item["title"], item["content"])
        print(sql)
        self.cursor = self.conn.cursor()
        try:
            self.cursor.execute(sql)
            self.conn.commit()
        except Exception as e:
            print(e)
            self.conn.rollback()
        return item

    def close_spider(self, spider):
        self.cursor.close()
        self.conn.close()
-----------------------------------------------------------------------
# item.py
import scrapy
class WangyiproItem(scrapy.Item):
    # define the fields for your item here like:
    title = scrapy.Field()
    content = scrapy.Field()
-----------------------------------------------------------------------
# spider/wangyi.py
import scrapy
from selenium import webdriver
from wangyipro.items import WangyiproItem


class WangyiSpider(scrapy.Spider):
    name = 'wangyi'
    # allowed_domains = ['www.xxx.com']
    start_urls = ['https://news.163.com/']
    model_list = []

    bro = webdriver.Chrome(executable_path=r"C:\Users\lzh\Downloads\chromedriver.exe")

    def parse(self, response):
        # 解析五個板塊對應的url
        li_list = response.xpath('//*[@id="index2016_wrap"]/div[1]/div[2]/div[2]/div[2]/div[2]/div/ul/li')
        model_index = [3, 4, 6, 7, 8]
        for index in model_index:
            li = li_list[index]
            # 5個板塊對應的url
            modul_url = li.xpath('./a/@href').extract_first()
            self.model_list.append(modul_url)
            # 對每個模塊對應的url發送請求

            yield scrapy.Request(modul_url, callback=self.parse_module)

    def parse_module(self, response):
        # 該方法中獲取到的response對象是沒有包含動態加載出的新聞數據(是一個不知足需求的response)
        div_list = response.xpath("/html/body/div/div[3]/div[4]/div[1]/div/div/ul/li/div/div")
        for div in div_list:
            detail_url = div.xpath("./a/@href").extract_first()
            title = div.xpath("./div/div[1]/h3/a/text()").extract_first()

            item = WangyiproItem()
            item["title"] = title
            yield scrapy.Request(url=detail_url, callback=self.parse_detail, meta={"item": item})

    def parse_detail(self, response):
        item = response.meta["item"]
        content = response.xpath('//*[@id="endText"]//text()').extract()
        content = "".join(content)
        item["content"] = content
        yield item

    def closed(self, spider):
        self.bro.quit()

做業二:圖片(懶加載)文件下載

- 圖片懶加載
	- 優化用戶體驗,應用到標籤的僞屬性,數據捕獲的時候必定是基於僞屬性進行的
- ImagePileline:專門是做用於數據下載和持久化存儲的管道類
# piplinex.py
import scrapy
from scrapy.pipelines.images import ImagesPipeline
class ImgproPipeline(ImagesPipeline):
    # 該方法是用來對媒體資源進行請求的(數據下載),參數item就是接收到的爬蟲類提交的item對象
    def get_media_requests(self, item, info):
        yield scrapy.Request(item["img_src"])

    # 指明數據的存儲路徑(其實這裏只是每一個文件的名稱,存儲文件須要在settings中設置)
    def file_path(self, request, response=None, info=None):
        return request.url.split("/")[-1]

    # 將item提交給下一個將被執行的管道類
    def item_completed(self, results, item, info):
        print(results) # 這result很關鍵,能夠打印看一下
        return item
-------------------------------------------------------------
# spider/img.py
import scrapy
from imgpro.items import ImgproItem
class ImgSpider(scrapy.Spider):
    name = 'img'
    # allowed_domains = ['www.xx.com']
    start_urls = ['http://sc.chinaz.com/tupian/xingganmeinvtupian.html']

    def parse(self, response):
        div_list = response.xpath('//*[@id="container"]/div')
        for div in div_list:
            img_src = div.xpath('./div/a/img/@src2').extract_first()
            # item = ImgproItem()
            item = ImgproItem()
            img_src = img_src.replace("_s", "")
            item["img_src"] = img_src
            yield item
 ---------------------------------settings.py中------------------------------
# 圖片存儲文件夾的路徑
IMAGES_STORE = "./imgLibs"
# "生成圖片縮略圖,添加設置"
IMAGES_THUMBS = {
   'big': (600, 600),
}
# 下載圖片大小的
# IMAGES_MIN_WIDTH = 110
# IMAGES_MIN_HEIGHT = 110

# 90天的圖片失效期限
IMAGES_EXPIRES = 90
# 啓用AutoThrottle擴展
AUTOTHROTTLE_ENABLED = True
# 初始下載延遲(單位:秒)
AUTOTHROTTLE_START_DELAY = 5
# 在高延遲狀況下最大的下載延遲(單位秒)
AUTOTHROTTLE_MAX_DELAY = 60
# 設置 Scrapy應該與遠程網站並行發送的平均請求數, 目前是以1個併發請求數
AUTOTHROTTLE_TARGET_CONCURRENCY = 1.0
# 啓用AutoThrottle調試模式
#AUTOTHROTTLE_DEBUG = False

CrawlSpider深度爬取:

- 一種基於scrapy進行全站數據爬取的一種新的技術手段
- CrawlSpider就是一個Spider的子類
	- 鏈接提取器:LinkExtractor
    - 規則解析器:Rule
- 使用流程
	- 新建一個工程
    - cd 工程中
    - 新建一個爬蟲文件:scrapy genspider -t crawl SpiderName www.xxx.com
# spider/sun.py
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from sunpro.items import SunproItem, SunproDetailItem

class SunSpider(CrawlSpider):
    name = 'sun'
    # allowed_domains = ['www.xxx.com']
    start_urls = ['http://wz.sun0769.com/political/index/supervise?page=1']

    # 實例化一個鏈接提取器
    # 做用:根據制定規則(allow="正則")進行制定鏈接的提取
    # link = LinkExtractor(allow=r'page=\d+')
    link = LinkExtractor(allow=r'supervise\?page=\d+')

    # 獲取詳情頁鏈接
    link_detail = LinkExtractor(allow=r"political/politics/index\?id=\d+")
    rules = (
        # 將link做用到了Rule構造方法的參數1中
        # 做用:將鏈接提取器取到的鏈接進行請求發送且根據指定的規則對請求到的數據進行解析

        Rule(link, callback='parse_item', follow=False),
        # follow =True : 將鏈接提取器繼續做用到,鏈接提取器提取到的鏈接所對應的頁面
        Rule(link_detail, callback='parse_detail', follow=False),
    )

    def parse_item(self, response):
        # xpath表達式中是不能夠出下tbody
        li_list = response.xpath('/html/body/div[2]/div[3]/ul/li')
        for li in li_list:
            title = li.xpath('./span[3]/a/text()').extract_first()
            num = li.xpath('./span[1]/text()').extract_first()
            item = SunproItem()
            item["title"] = title
            item["num"] = num
            yield item

    def parse_detail(self, response):
        num = response.xpath('/html/body/div[3]/div[2]/div[2]/div[1]/span[4]/text()').extract_first()
        num = num.split(":")[-1]
        content = response.xpath('/html/body/div[3]/div[2]/div[2]/div[2]/pre/text()').extract_first()
        item = SunproDetailItem()
        item["content"] = content
        item["num"] = num
        yield item

------------------------------------------------------------------------------
# pipline.py
class SunproPipeline(object):
    def process_item(self, item, spider):
        if item.__class__.__name__ == "SunproItem":
            title = item["title"]
            num = item["num"]
            print(item)
        else:
            content = item["content"]
            num = item["num"]
            print(item)
        return item
------------------------------------------------------------------------------
# items.py
import scrapy
class SunproItem(scrapy.Item):
    title = scrapy.Field()
    num = scrapy.Field()
class SunproDetailItem(scrapy.Item):
    content = scrapy.Field()
    num = scrapy.Field()

分佈式:

- 概念:須要搭建一個分佈式的機羣,而後再機羣的每個電腦中執行同一組程序,讓其對某一個網站的數據進行聯合分佈爬取
- 原生的scrapy框架是不能夠實現分佈式?
	- 由於調度器是不能夠被共享
    - 管道不能夠被共享
- 如何實現分佈式?
	- scrapy +scrapy-redis 實現分佈式
- scrapy-redis組件的做用是什麼
	- 能夠提供被共享的調度器和管道
    - 特性:數據只能夠存儲在redis數據庫
- 分佈式的實現流程:
	- 1.pip install scrapy-redis
    - 2.建立工程
    - 3.cd 工程目錄
    - 4.建立爬蟲文件(a.建立基於spider的爬蟲文件, b.建立基於crawlSpider的爬蟲文件)
    - 5.修改爬蟲類
    	
    	- 導包:from scrapy_redis.spiders import RedisCrawlSpider
	    - 修改繼承類:class FbsSpider(RedisCrawlSpider):
   		- 註釋:allowed_domains , start_urls 註釋掉
   		- 添加一個屬性:redis_key = "fbsQueue"  # 表示是能夠被共享的調度器隊列名稱
  		- 編寫爬蟲文件:(常規操做)
   	- 6. settings配置文件中配置
    	- UA假裝
        - robots
        - 管道的指定:
        	ITEM_PIPELINES = {
                       'scrapy_redis.pipelines.RedisPipeline': 400,}
        - 指定調度器:
        	# 使用scrapy-redis組件的去重隊列
            DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
            # 使用scrapy-redis組件本身的調度器
            SCHEDULER = "scrapy_redis.scheduler.Scheduler"
            # 配置調度器是否要持久化,也就是當爬蟲結束了,要不要清空Redis中請求隊列和去重指定的set。若是是True表示要持久化,就不清空數據,不然清空數據
            SCHEDULER_PERSIST = True
        - 指定redis數據庫:
        	REDIS_HOST = 'redis服務的ip地址'
            REDIS_PORT = 6379
            REDIS_ENCODING = ‘utf-8’
            REDIS_PARAMS = {‘password’:’123456’}
	    - redis的配置文件進行配置redis.window.conf(隨便賦值一個redis配置文件重命名)
        	- 關閉默認綁定:
            	56line:# bing 127.0.0.1
            - 關閉保護模式: 
            	75line:protected-mode yes  # 默認只能讀不能寫(須要修改no)
        - 啓動redis的客戶端以及服務端
         	redis-server redis.window.conf
        	redis-cli
       	- 啓動程序:
        	cd fbspro
            cd spider # 進入爬蟲文件的目錄中
        	scrapy runspider fbs.py
        - 向調度器的隊列中扔入起始url:
        	- 隊列是存在與redis中
            - 開啓redis的客戶端: lpush fbsQueue start_url
            # 這個時候可能會連不上redis,修改bind 0.0.0.0 ,同時protected-mode no,接着就是須要關閉防火牆,systemctl stop firewalld;  setenforce 0

分佈式默認配置:

ITEM_PIPELINES = {
    'scrapy_redis.pipelines.RedisPipeline': 400
}

# 使用scrapy-redis組件的去重隊列
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
# 使用scrapy-redis組件本身的調度器
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
# 配置調度器是否要持久化,也就是當爬蟲結束了,要不要清空Redis中請求隊列和去重指定的set。若是是True表示要持久化,就不清空數據,不然清空數據
SCHEDULER_PERSIST = True

REDIS_HOST = '192.168.31.199'
REDIS_PORT = 6379  

# 分佈式爬蟲:注意地方有兩點,其中爬蟲文件中的redis_key須要一致,同時settings中的redis_host須要需改成統一的。
# 可使用

增量式:

- 概念:用於檢測網站數據更新狀況
- 核心機制:去重,redis的set實現去重

增量式代碼:

import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
import redis
from zljpro.items import ZljproItem


class ZlsSpider(CrawlSpider):
    conn = redis.Redis(host="192.168.31.199", port=6379)
    name = 'zls'
    # allowed_domains = ['www.xxx.com']
    start_urls = ['https://www.4567kan.com/index.php/vod/show/class/%E5%96%9C%E5%89%A7/id/1.html']

    link = LinkExtractor(allow=r'page/\d+\.html')
    rules = (
        Rule(link, callback='parse_item', follow=True),
    )

    def parse_item(self, response):
        li_list = response.xpath('/html/body/div[1]/div/div/div/div[2]/ul/li')
        for li in li_list:
            name = li.xpath('./div/a/@title').extract_first()
            # 關於這裏爲何不使用鏈接提取器,使用由於rules會對每個提取到的鏈接都發請求,而
            # 咱們增量式的意義在於只對新增的數據發起請求,因此這裏咱們手動發送,並將詳情頁的url插
            # 入redis的set中,進行去重爬取

            detail_url = 'https://www.4567kan.com' + li.xpath('./div/a/@href').extract_first()
            # 能夠將爬取過得電影的詳情頁的url記錄起來
            # if ex == 0 插入失敗,若是ex == 1 插入成功
            ex = self.conn.sadd("movie_detail_urls", detail_url)

            item = ZljproItem()
            item["name"] = name
            if ex == 1:
                print("正在爬取新數據")
                yield scrapy.Request(url=detail_url, callback=self.parse_detail, meta={"item": item})
            else:
                print("數據暫未更新")

    def parse_detail(self, response):
        item = response.meta['item']
        desc = response.xpath('/html/body/div[1]/div/div/div/div[2]/p[5]/span[3]/text()').extract_first()
        item['desc'] = desc
        yield item
        
-----------------------------------------------pipeline.py
class ZljproPipeline(object):
    def process_item(self, item, spider):
        conn = spider.conn
        conn.lpush("movieData", item)
        return item

反爬機制總結:

- rebots
- UA假裝
- 驗證碼
- 代理
- cookie
- 動態變化的請求參數
- js加密
- js混淆
- 圖片懶加載
- 動態數據捕獲
- selenium:規避檢測
相關文章
相關標籤/搜索