python爬虫之ip代理参数/动态加载数据抓取

文章目录

    • 前情回顾
      • requests.get()参数
      • 常见的反爬机制及处理方式
    • 今日笔记
      • 代理参数-proxies
      • 控制台抓包
      • requests.post()参数
      • 有道翻译破解案例(post)
      • python中正则处理headers和formdata
      • 民政部网站数据抓取
      • 动态加载数据抓取-Ajax
      • 豆瓣电影数据抓取案例
      • 今日任务

前情回顾

requests.get()参数

1、url
2、params -> {} :查询参数 Query String
3、proxies -> {}
   proxies = {
    'http':'http://1.1.1.1:8888',
	  'https':'https://1.1.1.1:8888'
   }
4、auth -> ('tarenacode','code_2013')
5、verify -> True/False
6、timeout

常见的反爬机制及处理方式

1、Headers反爬虫 :Cookie、Referer、User-Agent
   解决方案: 通过F12获取headers,传给requests.get()方法
        
2、IP限制 :网站根据IP地址访问频率进行反爬,短时间内限制IP访问
   解决方案: 
        1、构造自己IP代理池,每次访问随机选择代理,经常更新代理池
        2、购买开放代理或私密代理IP
        3、降低爬取的速度
        
3、User-Agent限制 :类似于IP限制
   解决方案: 构造自己的User-Agent池,每次访问随机选择
        
4、对响应内容做处理
   解决方案: 打印并查看响应内容,用xpath或正则做处理

今日笔记

代理参数-proxies

  • 定义
1、定义: 代替你原来的IP地址去对接网络的IP地址。
2、作用: 隐藏自身真实IP,避免被封。

普通代理

  • 获取代理IP网站
西刺代理、快代理、全网代理、代理精灵、... ...
  • 参数类型
1、语法结构
   	proxies = {
       	'协议':'协议://IP:端口号'
   	}
2、示例
    proxies = {
    	'http':'http://IP:端口号',
    	'https':'https://IP:端口号'
	}
  • 示例

使用免费普通代理IP访问测试网站: http://httpbin.org/get

import requests

url = 'http://httpbin.org/get'
headers = {
    'User-Agent':'Mozilla/5.0'
}
# 定义代理,在代理IP网站中查找免费代理IP
proxies = {
    'http':'http://112.85.164.220:9999',
    'https':'https://112.85.164.220:9999'
}
html = requests.get(url,proxies=proxies,headers=headers,timeout=5).text
print(html)

思考: 建立一个自己的代理IP池,随时更新用来抓取网站数据

1、从西刺代理IP网站上,抓取免费代理IP
2、测试抓取的IP,可用的保存在文件中

思考 - 代码实现

import requests
url="https://www.xicidaili.com/nn/"
# url="https://www.xicidaili.com/nn/"
proxies={
    "http":"http://309435365:[email protected]:17714",
    "https":"https://309435365:[email protected]:17714"
}
headers = { 'User-Agent':'Mozilla/5.0' }
html=requests.get(url=url,headers=headers,proxies=proxies).text
print(html)

私密代理

  • 语法格式
1、语法结构
proxies = {
    '协议':'协议://用户名:密码@IP:端口号'
}

2、示例
proxies = {
	'http':'http://用户名:密码@IP:端口号',
  'https':'https://用户名:密码@IP:端口号'
}

示例代码

import requests
url = 'http://httpbin.org/get'
proxies = {
    'http':'http://309435365:[email protected]:17509',
    'https':'https://309435365:[email protected]:17509'
}
headers = {
    'User-Agent' : 'Mozilla/5.0',
}

html = requests.get(url,proxies=proxies,headers=headers,timeout=5).text
print(html)

控制台抓包

1、打开浏览器,F12打开控制台,找到Network选项卡
2、控制台常用选项
   1、Network: 抓取网络数据包
        1、ALL: 抓取所有的网络数据包
        2、XHR:抓取异步加载的网络数据包
        3、JS : 抓取所有的JS文件
   2、Sources: 格式化输出并打断点调试JavaScript代码,助于分析爬虫中一些参数
   3、Console: 交互模式,可对JavaScript中的代码进行测试
3、抓取具体网络数据包后
   1、单击左侧网络数据包地址,进入数据包详情,查看右侧
   2、右侧:
       1、Headers: 整个请求信息
          General、Response Headers、Request Headers、Query String、Form Data
       2、Preview: 对响应内容进行预览
       3、Response:响应内容

requests.post()参数

  • 适用场景
Post类型请求的网站
  • 参数-data
response = requests.post(url,data=data,headers=headers)
# data :post数据(Form表单数据-字典格式)
  • 请求方式的特点
# 一般
GET请求 : 参数在URL地址中有显示
POST请求: Form表单提交数据

有道翻译破解案例(post)

  • 目标
破解有道翻译接口,抓取翻译结果
# 结果展示
请输入要翻译的词语: elephant
翻译结果: 大象
**************************
请输入要翻译的词语: 喵喵叫
翻译结果: mews
  • 实现步骤
1、浏览器F12开启网络抓包,Network-All,页面翻译单词后找Form表单数据
2、在页面中多翻译几个单词,观察Form表单数据变化(有数据是加密字符串)
3、刷新有道翻译页面,抓取并分析JS代码(本地JS加密)
4、找到JS加密算法,用Python按同样方式加密生成加密数据
5、将Form表单数据处理为字典,通过requests.post()的data参数发送
  • 具体实现

  • 1、开启F12抓包,找到Form表单数据如下:

i: 喵喵叫
from: AUTO
to: AUTO
smartresult: dict
client: fanyideskweb
salt: 15614112641250
sign: 94008208919faa19bd531acde36aac5d
ts: 1561411264125
bv: f4d62a2579ebb44874d7ef93ba47e822
doctype: json
version: 2.1
keyfrom: fanyi.web
action: FY_BY_REALTlME
  • 2、在页面中多翻译几个单词,观察Form表单数据变化
salt: 15614112641250
sign: 94008208919faa19bd531acde36aac5d
ts: 1561411264125
bv: f4d62a2579ebb44874d7ef93ba47e822
# 但是bv的值不变
  • 3、一般为本地js文件加密,刷新页面,找到js文件并分析JS代码
# 方法1
Network - JS选项 - 搜索关键词salt
# 方法2
控制台右上角 - Search - 搜索salt - 查看文件 - 格式化输出
# 最终找到相关JS文件 : fanyi.min.js
  • 4、打开JS文件,分析加密算法,用Python实现
# ts : 经过分析为13位的时间戳,字符串类型
js代码实现:  "" + (new Date).getTime()
python实现:  str(int(time.time()*1000))

# salt
js代码实现:  ts+parseInt(10 * Math.random(), 10);
python实现:  ts+str(random.randint(0,9))

# sign(设置断点调试,来查看 e 的值,发现 e 为要翻译的单词)
js代码实现: n.md5("fanyideskweb" + e + salt + "n%A-rKaT5fb[Gy?;N5@Tj")
python实现:
    from hashlib import md5
    string=""
    s=md5()
    s.update(string.encode())
    sign=s.hexdigest()
  • 5、代码实现
import requests, time, random
from hashlib import md5

from day01.User_Agent import getheaders


class YouDao:
    def __init__(self):
        # url一定要写F12抓到的post的地址
        self.url = 'http://fanyi.youdao.com/translate_o?smartresult=dict&smartresult=rule'
        self.headers = {
            # "Accept":"application/json, text/javascript, */*; q=0.01",
            # "Accept-Encoding":"gzip, deflate",
            # "Accept-Language":"zh-CN,zh;q=0.9,en;q=0.8",
            # "Connection":"keep-alive",
            # "Content-Length":"239",
            # "Content-Type":"application/x-www-form-urlencoded; charset=UTF-8",
            "Cookie": "P_INFO=xinwei_user; [email protected]; JSESSIONID=aaay3E4LlPWDr64vjDn3w; OUTFOX_SEARCH_USER_ID_NCOO=1784198643.0122821; ___rl__test__cookies=1571131720098",
            # "Host":"fanyi.youdao.com",
            # "Origin":"http://fanyi.youdao.com",
            "Referer": "http://fanyi.youdao.com/",
            "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.75 Safari/537.36",
            # "X-Requested-With":"XMLHttpRequest",
        }
        # self.proxies={}

    # 获取ts,salt,sign
    def get_ts_salt_sign(self, word):
        ts = str(random.randint(0, 9))
        salt = ts + str(int(time.time() * 1000))
        string = "fanyideskweb" + word + salt + "n%A-rKaT5fb[Gy?;N5@Tj"
        s = md5()
        s.update(string.encode())
        sign = s.hexdigest()

        return ts, salt, sign

    def attack(self, word):
        ts, salt, sign = self.get_ts_salt_sign(word)
        # form表单数据
        data = {
            "i": word,
            "from": "AUTO",
            "to": "AUTO",
            "smartresult": "dict",
            "client": "fanyideskweb",
            "salt": salt,
            "sign": sign,
            # "ts": ts,
            # "bv": "65313ac0ff6808a532a1d4971304070e",
            "doctype": "json",
            "version": "2.1",
            "keyfrom": "fanyi.web",
            "action": "FY_BY_REALTlME", }
        # 此处为post请求,一定使用requests.post
        # html=requests.post(
        #     url=self.url,
        #     data=data,
        #     headers=self.headers,
        #     # proxies=self.proxies
        # ).text    #返回值为字符串

        html = requests.post(
            url=self.url,
            data=data,
            headers=self.headers,
            # proxies=self.proxies
        ).json()
        # print(type(html),html)  #返回值为字典
        print("翻译结果:", html["translateResult"][0][0]["tgt"])
        # print(html)

    def run(self):
        word = input("请输入单词:")
        self.attack(word)


if __name__ == '__main__':
    y = YouDao()
    y.run()

python中正则处理headers和formdata

1、pycharm进入方法 :Ctrl + r ,选中 Regex
2、处理headers和formdata
  (.*): (.*)
  "$1": "$2",
3、点击 Replace All

民政部网站数据抓取

目标

1、URL: http://www.mca.gov.cn/ - 民政数据 - 行政区划代码
   即: http://www.mca.gov.cn/article/sj/xzqh/2019/
2、目标: 抓取最新中华人民共和国县以上行政区划代码

实现步骤

  • 1、从民政数据网站中提取最新行政区划代码链接
# 特点
1、最新的在上面
2、命名格式: 2019年X月中华人民共和国县以上行政区划代码
#a_list:[
a_list=p.xpath('a[@class="artitlelist"]')
	for a in a_list:
        title=a.get("title")
        if title.endswith("行政区快代码")
        #第一个满足条件的一定是最新的文章链接
        href=a.get("href")
        break

2、从二级页面链接中提取真实链接(反爬-响应内容中嵌入JS,指向新的链接)

1、向二级页面链接发请求得到响应内容,并查看嵌入的JS代码
2、正则提取真实的二级页面链接

3、真实链接中提取所需数据

4、代码实现

import re
import requests
from lxml import etree
from day01.User_Agent import getheaders

class MinZheng:
    def __init__(self):
        self.url = 'http://www.mca.gov.cn/article/sj/xzqh/2019/'
        self.headers = getheaders()

    # 获取最新链接
    def get_link(self):
        one_html = requests.get(url=self.url, headers=self.headers).text
        # xpath提取最新链接
        p = etree.HTML(one_html)
        link_list = p.xpath('//table//tr[2]/td[2]/a/@href')
        link = link_list[0] if link_list else None
        if link:
            link = 'http://www.mca.gov.cn/article/sj/xzqh/2019/' + ("/").join(link.split("/")[-2:])
            #http://www.mca.gov.cn/article/sj/xzqh/2019/201909/20190900019994.shtml  非真实链接
            print(link)
            self.parse_html(link)
        else:
            print("链接提取失败!")

    # 提取数据
    def parse_html(self, link):
        two_html = requests.get(url=link, headers=self.headers).text
        #保存跳转页面
        with open('cccz.html',"w") as f:
            f.write(two_html)
        p = etree.HTML(two_html)
        new1_html = p.xpath('//script/text()')[0]
        # print(new1_html)
        #提取跳转链接
        new2_html=re.findall('window.location.href="(.*?)"',new1_html)[0]
        # print(new2_html)
        two_new_html=requests.get(url=new2_html,headers=self.headers).text
        p = etree.HTML(two_new_html)
        tr_list = p.xpath('//tr[@height="19"]')
        for tr in tr_list:
            name = tr.xpath('./td[3]/text()')[0].strip()
            code = tr.xpath('./td[2]/text()')[0].strip()
            print(name, code)

    # 入口函数
    def run(self):
        self.get_link()


if __name__ == '__main__':
    m = MinZheng()
    m.run()

扩展

1、建立增量爬虫 - 网站有更新时抓取,否则不抓(设置指纹函数)
2、所抓数据存到数据库,按照层级关系分表存储 - 省、市、县表

动态加载数据抓取-Ajax

  • 特点
1、右键 -> 查看网页源码中没有具体数据
2、滚动鼠标滑轮或其他动作时加载,或者页面局部刷新,页面才能加载出来
  • 抓取
1、F12打开控制台,页面动作抓取网络数据包
2、抓取json文件URL地址
# 控制台中 XHR :异步加载的数据包
# XHR -> QueryStringParameters(查询参数)

豆瓣电影数据抓取案例

  • 目标
1、地址: 豆瓣电影 - 排行榜 - 剧情
2、目标: 电影名称、电影评分
  • F12抓包(XHR)
1、Request URL(基准URL地址) :https://movie.douban.com/j/chart/top_list?
2、Query String(查询参数)
# 抓取的查询参数如下:
type: 13 # 电影类型
interval_id: 100:90
action: ''
start: 0  # 每次加载电影的起始索引值 0 20 40 60 
limit: 20 # 每次加载的电影数量
  • 代码实现 - 全站抓取 - 完美接口 - 指定类型所有电影信息
import re
import requests, json
from fake_useragent import UserAgent

class DouBan:
    def __init__(self):
        self.url = "https://movie.douban.com/j/chart/top_list?"
        self.i = 1

    def parse_html(self, params):
        html = requests.get(
            url=self.url,
            params=params,
            headers={"User-Agent": UserAgent().random}
        ).text
        # json.loads():json数据->python数据类型
        html = json.loads(html)
        # print(html)
        item = {}
        for film in html:
            item["name"] = film["title"]
            item["score"] = film["score"]
            self.i += 1
            print(item)

    def run(self):

        type_dic = self.get_name_type()
        name = input("\n请输入类型:")
        typ = type_dic[name]
        totle = self.get_totle(typ)
        for page in range(0, totle, 20):
            params = {
                "type": typ,
                "interval_id": "100:90",
                "action": "",
                "start": str(page),
                "limit": "20"
            }
            self.parse_html(params)
        print(self.i)

    def get_totle(self, typ):
        url = 'https://movie.douban.com/j/chart/top_list_count?type={}&interval_id=100%3A90'.format(typ)
        html = requests.get(url=url, headers={"User-Agent": UserAgent().random}).json()
        totle = html["total"]

        return totle

    def get_name_type(self):
        url = 'https://movie.douban.com/chart'
        html = requests.get(url=url, headers={"User-Agent": UserAgent().random}).text
        p = re.compile('', re.S)
        r_list = p.findall(html)
        type_dict = {}
        print("电影类型:")
        for r in r_list:
            type_dict[r[0]] = r[1]
            print(r[0],end=" ")

        return type_dict

if __name__ == '__main__':
    d = DouBan()
    d.run()

今日任务

1、有道翻译案例复写一遍
2、抓取腾讯招聘数据(两级页面 - 职位名称、岗位职责、工作要求)
3、抓取百度图片 - 所有,而不是304、民政部数据抓取案例完善
   # 1、将抓取的数据存入数据库,最好分表按照层级关系去存
   # 2、增量爬取时表中数据也要更新

你可能感兴趣的:(爬虫,ip代理,动态加载数据抓取)