python抓取动态网页(常见的反爬机制及处理方式(一)|)

优采云 发布时间: 2021-09-13 11:13

  python抓取动态网页(常见的反爬机制及处理方式(一)|)

  常见的防爬机制及处理方法

  1、Headers Anti-Crawler:Cookie、Referer、User-Agent

  解决方法:通过 F12 获取 headers 并将它们传递给 requests.get() 方法

  2、IP 限制:网站according

  常见的防爬机制及处理方法

  1、Headers Anti-Crawler:Cookie、Referer、User-Agent

  解决方法:通过 F12 获取 headers 并将它们传递给 requests.get() 方法

  2、IP限制:网站根据IP地址访问频率进行反爬,短时间IP访问

  解决方案:

  1、构建自己的IP代理池,每次访问随机选择代理,并经常更新代理池

  2、购买开放代理或私有代理IP

  3、降低爬取速度

  3、User-Agent 限制:类似于IP限制

  解决方案:构建自己的User-Agent池,每次访问随机选择

  5、查询参数或表单数据的认证(salt、sign)

  解决方法:找到JS文件,分析JS处理方式,用Python同样处理

  6、处理响应内容

  解决方法:打印查看响应内容,使用xpath或者常规处理

  python 中对 headers 和 formdata 的常规处理

  1、pycharm 访问方式:Ctrl + r,选择Regex

  2、Process 标头和表单数据

  (.*): (.*)

  "1":"1":"2",

  3、点击全部替换

  民政部网站数据采集

  目标:抓取中华人民共和国最新的县级以上行政区划代码

  网址:-民政数据-行政部门代码

  实施步骤

  1、民政数据中提取最新行政区划代码链接网站

  最新在上,命名格式:X,2019,中华人民共和国县级以上行政区划代码

  

import requests

from lxml import etree

import re

url = 'http://www.mca.gov.cn/article/sj/xzqh/2019/'

headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36'}

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

parse_html = etree.HTML(html)

article_list = parse_html.xpath('//a[@class="artitlelist"]')

for article in article_list:

title = article.xpath('./@title')[0]

# 正则匹配title中包含这个字符串的链接

if title.endswith('代码'):

# 获取到第1个就停止即可,第1个永远是最新的链接

two_link = 'http://www.mca.gov.cn' + article.xpath('./@href')[0]

print(two_link)

break

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

  向二级页面链接发送请求获取响应内容,查看嵌入的JS代码

  定期提取真实二级页面链接

  

# 爬取二级“假”链接

two_html = requests.get(two_link, headers=headers).text

# 从二级页面的响应中提取真实的链接(此处为JS动态加载跳转的地址)

new_two_link = re.findall(r'window.location.href="(.*?)" rel="external nofollow" rel="external nofollow" ', two_html, re.S)[0]

  3、在数据库表中查询此链接是否被爬取过,构建增量爬虫

  在数据库中创建一个版本表来存储爬取的链接

  检查程序每次执行时和版本表中的记录,看是否被爬取过

  

cursor.execute('select * from version')

result = self.cursor.fetchall()

if result:

if result[-1][0] == two_link:

print('已是最新')

else:

# 有更新,开始抓取

# 将链接再重新插入version表记录

  4、代码实现

  

import requests

from lxml import etree

import re

import pymysql

class GovementSpider(object):

def __init__(self):

self.url = 'http://www.mca.gov.cn/article/sj/xzqh/2019/'

self.headers = {'User-Agent': 'Mozilla/5.0'}

# 创建2个对象

self.db = pymysql.connect('127.0.0.1', 'root', '123456', 'govdb', charset='utf8')

self.cursor = self.db.cursor()

# 获取假链接

def get_false_link(self):

html = requests.get(url=self.url, headers=self.headers).text

# 此处隐藏了真实的二级页面的url链接,真实的在假的响应网页中,通过js脚本生成,

# 假的链接在网页中可以访问,但是爬取到的内容却不是我们想要的

parse_html = etree.HTML(html)

a_list = parse_html.xpath('//a[@class="artitlelist"]')

for a in a_list:

# get()方法:获取某个属性的值

title = a.get('title')

if title.endswith('代码'):

# 获取到第1个就停止即可,第1个永远是最新的链接

false_link = 'http://www.mca.gov.cn' + a.get('href')

print("二级“假”链接的网址为", false_link)

break

# 提取真链接

self.incr_spider(false_link)

# 增量爬取函数

def incr_spider(self, false_link):

self.cursor.execute('select url from version where url=%s', [false_link])

# fetchall: (('http://xxxx.html',),)

result = self.cursor.fetchall()

# not result:代表数据库version表中无数据

if not result:

self.get_true_link(false_link)

# 可选操作: 数据库version表中只保留最新1条数据

self.cursor.execute("delete from version")

# 把爬取后的url插入到version表中

self.cursor.execute('insert into version values(%s)', [false_link])

self.db.commit()

else:

print('数据已是最新,无须爬取')

# 获取真链接

def get_true_link(self, false_link):

# 先获取假链接的响应,然后根据响应获取真链接

html = requests.get(url=false_link, headers=self.headers).text

# 从二级页面的响应中提取真实的链接(此处为JS动态加载跳转的地址)

re_bds = r'window.location.href="(.*?)" rel="external nofollow" rel="external nofollow" '

pattern = re.compile(re_bds, re.S)

true_link = pattern.findall(html)[0]

self.save_data(true_link) # 提取真链接的数据

# 用xpath直接提取数据

def save_data(self, true_link):

html = requests.get(url=true_link, headers=self.headers).text

# 基准xpath,提取每个信息的节点列表对象

parse_html = etree.HTML(html)

tr_list = parse_html.xpath('//tr[@height="19"]')

for tr in tr_list:

code = tr.xpath('./td[2]/text()')[0].strip() # 行政区划代码

name = tr.xpath('./td[3]/text()')[0].strip() # 单位名称

print(name, code)

# 主函数

def main(self):

self.get_false_link()

if __name__ == '__main__':

spider = GovementSpider()

spider.main()

  动态加载数据捕获-Ajax

  特点

  右键->查看网页源代码,无需具体数据

  滚动鼠标滚轮或其他动作时加载

  楣

  F12打开控制台,选择XHR异步加载数据包,找到抓取网络数据包的页面动作

  通过XHR获取json文件的URL地址-->Header-->General-->Request URL

  通过XHR-->Header-->查询字符串参数

  豆瓣电影数据采集案例

  目标

  地址:豆瓣电影-排行榜-剧情

  type_name=%E5%89%A7%E6%83%85&type=11&interval_id=100:90&action=

  目标:抓取电影名称、电影评分

  F12 数据包捕获 (XHR)

  1、Request URL(基本 URL 地址):

  2、Query String Paramaters(查询参数)

  

# 查询参数如下:

type: 13 # 电影类型

interval_id: 100:90

action: '[{},{},{}]'

start: 0 # 每次加载电影的起始索引值

limit: 20 # 每次加载的电影数量

  json 文件位于以下地址:

  基本 URL 地址 + 查询参数

  ''+'type=11&interval_id=100%3A90&action=&start=20&limit=20'

  代码实现

  

import requests

import time

from fake_useragent import UserAgent

class DoubanSpider(object):

def __init__(self):

self.base_url = 'https://movie.douban.com/j/chart/top_list?'

self.i = 0

def get_html(self, params):

headers = {'User-Agent': UserAgent().random}

res = requests.get(url=self.base_url, params=params, headers=headers)

res.encoding = 'utf-8'

html = res.json() # 将json格式的字符串转为python数据类型

self.parse_html(html) # 直接调用解析函数

def parse_html(self, html):

# html: [{电影1信息},{电影2信息},{}]

item = {}

for one in html:

item['name'] = one['title'] # 电影名

item['score'] = one['score'] # 评分

item['time'] = one['release_date'] # 打印测试

# 打印显示

print(item)

self.i += 1

# 获取电影总数

def get_total(self, typ):

# 异步动态加载的数据 都可以在XHR数据抓包

url = 'https://movie.douban.com/j/chart/top_list_count?type={}&interval_id=100%3A90'.format(typ)

ua = UserAgent()

html = requests.get(url=url, headers={'User-Agent': ua.random}).json()

total = html['total']

return total

def main(self):

typ = input('请输入电影类型(剧情|喜剧|动作):')

typ_dict = {'剧情': '11', '喜剧': '24', '动作': '5'}

typ = typ_dict[typ]

total = self.get_total(typ) # 获取该类型电影总数量

for page in range(0, int(total), 20):

params = {

'type': typ,

'interval_id': '100:90',

'action': '',

'start': str(page),

'limit': '20'}

self.get_html(params)

time.sleep(1)

print('爬取的电影的数量:', self.i)

if __name__ == '__main__':

spider = DoubanSpider()

spider.main()

  腾讯招聘数据抓取(Ajax)

  确定 URL 地址和目标

  网址:百度搜索腾讯招聘-查看职位

  目标:职位、工作职责、工作要求

  需求与分析

  查看网页源代码,我们知道所有需要的数据都是Ajax动态加载的

  通过F12获取网络数据包进行分析

  一级页面爬取数据:职位

  在二级页面抓取数据:工作职责、工作要求

  一级页面json地址(pageIndex在变化,时间戳不检查)

  {}&pageSize=10&language=zh-cn&area=cn

  二级页面地址(postId正在变化,可以在主页面获取)

  {}&language=zh-cn

  useragents.py 文件

  

ua_list = [

'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.163 Safari/535.1',

'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:6.0) Gecko/20100101 Firefox/6.0',

'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; InfoPath.3)',

]

  

import time

import json

import random

import requests

from useragents import ua_list

class TencentSpider(object):

def __init__(self):

self.one_url = 'https://careers.tencent.com/tencentcareer/api/post/Query?timestamp=1563912271089&countryId=&cityId=&bgIds=&productId=&categoryId=&parentCategoryId=&attrId=&keyword=&pageIndex={}&pageSize=10&language=zh-cn&area=cn'

self.two_url = 'https://careers.tencent.com/tencentcareer/api/post/ByPostId?timestamp=1563912374645&postId={}&language=zh-cn'

self.f = open('tencent.json', 'a') # 打开文件

self.item_list = [] # 存放抓取的item字典数据

# 获取响应内容函数

def get_page(self, url):

headers = {'User-Agent': random.choice(ua_list)}

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

html = json.loads(html) # json格式字符串转为Python数据类型

return html

# 主线函数: 获取所有数据

def parse_page(self, one_url):

html = self.get_page(one_url)

item = {}

for job in html['Data']['Posts']:

item['name'] = job['RecruitPostName'] # 名称

post_id = job['PostId'] # postId,拿postid为了拼接二级页面地址

# 拼接二级地址,获取职责和要求

two_url = self.two_url.format(post_id)

item['duty'], item['require'] = self.parse_two_page(two_url)

print(item)

self.item_list.append(item) # 添加到大列表中

# 解析二级页面函数

def parse_two_page(self, two_url):

html = self.get_page(two_url)

duty = html['Data']['Responsibility'] # 工作责任

duty = duty.replace('\r\n', '').replace('\n', '') # 去掉换行

require = html['Data']['Requirement'] # 工作要求

require = require.replace('\r\n', '').replace('\n', '') # 去掉换行

return duty, require

# 获取总页数

def get_numbers(self):

url = self.one_url.format(1)

html = self.get_page(url)

numbers = int(html['Data']['Count']) // 10 + 1 # 每页有10个推荐

return numbers

def main(self):

number = self.get_numbers()

for page in range(1, 3):

one_url = self.one_url.format(page)

self.parse_page(one_url)

# 保存到本地json文件:json.dump

json.dump(self.item_list, self.f, ensure_ascii=False)

self.f.close()

if __name__ == '__main__':

start = time.time()

spider = TencentSpider()

spider.main()

end = time.time()

print('执行时间:%.2f' % (end - start))

  以上是本文的全部内容。希望对大家的学习有所帮助,也希望大家多多支持Scripthome。

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线