30.爬虫总结

https://www.cnblogs.com/bobo-zhang/p/9645715.html

快捷键:

"""
插入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协议:遵从或者不遵从

抓包工具:fiddler


无参请求:

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:

https://www.cnblogs.com/bobo-zhang/p/9682516.html bs4以及xpath使用

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使用:

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))

中文乱码问题:

案例一:中文乱码

# 解决爬虫中文问题
# 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)

案例二:中文乱码

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")

案例三:加密图片

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)

案例四:简历爬取

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")

案例五:城市信息

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标签:

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

https://www.cnblogs.com/bobo-zhang/p/10069004.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:规避检测
posted @ 2020-04-10 16:44  楠海  阅读(440)  评论(0编辑  收藏  举报