58同城 反爬虫机制及处理

58同城 反爬虫机制及处理

  1. 字体反爬机制
    问题:

字体反爬也就是自定义字体反爬通过调用自定义的ttf文件来渲染网页中的文字,而网页中的文字不再是文字,而是相应的字体编码,通过复制或者简单的采集是无法采集到编码后的文字内容!必须通过程序去处理才能达到采集成本。
例如:也就是通过自定义字体来自定义字符与渲染图形的映射。比如,字符 a 实际渲染的是 9,那么如果 HTML 中的数字是 aaa,实际显示就是 999。

网页显示与源代码出现不一致的情况
这里58同城将数字(比较敏感的元素,包括价格,面积大小)用自定义的ttf文件进行了渲染(而且该文件通过base64进行加密,包含数字到相应的unicode字符之间的映射信息),从而产生类似于乱码的情况。

Base64:是从二进制到字符的过程,可用于在HTTP环境下传递较长的标识信息。采用Base64编码具有不可读性,需要解码后才能阅读。

处理:
(1)在网页源码中找到相应的base64编码的文件信息,对其进行解码并存储为ttf格式文件
58同城 反爬虫机制及处理_第1张图片

font_face = 'AAEAAAALAIAAAwAwR1NVQiCLJXoAAAE4AA...AAA' # 此次省略
b = base64.b64decode(font_face)  # 对映射表进行解码
with open('58.ttf', 'wb') as f:
    f.write(b)

(2)可以利用FontCreate软件得出每一个数字对应unicode字符(映射关系),存入字典中(注意字典中的value也得换成unicode字符)
58同城 反爬虫机制及处理_第2张图片


```python
diction = {
     '\u9a4b': '\u0030', '\u9f92': '\u0031', '\u993c': '\u0032',
           '\u9ea3': '\u0033', '\u9fa4': '\u0034', '\u9fa5': '\u0035',
           '\u958f': '\u0036', '\u9e3a': '\u0037', '\u9476': '\u0038',
           '\u9f64': '\u0039'}  # 分别是某unicode字符与数字之间的映射

(3)最后以该字典为对照,将爬取到的包含数字的文本,转换成我们可以看懂的数字。

TIP:通过上述方法并没有真在知道数字与字符之间的映射关系的规律(函数关系),这个过程的寻找需要花费时间。

  • 频繁请求爬虫检测机制
    问题:
    在爬取过程中,爬虫模拟浏览器对58同城服务端频繁发出网页请求以获取数据,在此过程中,使用同一IP频繁访问就会被服务端判定为爬虫,此时返回网页会出现人机验证信息:
    58同城 反爬虫机制及处理_第3张图片
    处理:
  • 使用IP代理:

我们给浏览器设置代理,我们访问目标网站的请求会先经过代理服务器,然后由代理服务器将请求转化到目标网站,这样每次使用不同的IP地址访问,让服务器以为不是来自同一用户的请求,一定程度降低了被发现的风险。

  • 代理IP的来源:

从免费ip代理网站获取
分为三个部分:分别是获取ip和端口;对ip、端口进行验证(判断ip是否可用);存储可用IP
优势:免费
劣势:大部分免费IP不可用
购买IP代理:
直接利用提过收费方提供的API接口,获取IP
优势:花了钱的肯定是有用的,获取方便,IP存活率高
劣势:要RMB

# coding:utf-8
import requests
from bs4 import BeautifulSoup
from fake_useragent import UserAgent
import random

ROOT_URL = 'http://www.89ip.cn/index_'

ip_list = []


def judge(ip_port):
    proxies = {
     'http': 'http://'+ip_port, 'https': 'https://'+ip_port}
    try:
        res = requests.get('https://www.baidu.com', headers=UserAgent.random, proxies=proxies)
    except Exception:
        return False
    else:
        if 200 <= res.status_code < 300:
            # 返回的状态码在200到300之间表示请求成功
            return True
        else:
            print('请求失败')
            return False


# 第三步:从ip_list中随机获取一个ip
def get_random_ip():
    while ip_list:
        ip_port = random.choice(ip_list)
        result1 = judge(ip_port)
        if result1 is not False:
            proxies = {
     'http': 'http://'+ip_port, 'https': 'https://'+ip_port}
            return proxies
        else:
            ip_list.remove(ip_port)
    return None


class IpPool(object):

    def _get_ip(self, n):
        try:
            headers = {
     'User-Agent': UserAgent.random}
        except AttributeError:
            headers = {
     
                'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
                              'AppleWebKit/537.36 (KHTML, like Gecko) '
                              'Chrome/84.0.4147.105 Safari/537.36 '}
        url = ROOT_URL + '{page}'.format(page=n)
        response = requests.get(url, headers=headers)
        soup = BeautifulSoup(response.text, 'html.parser')
        items = soup.find_all('td')
        for i, j in zip(items[::5], items[1::5]):
            ip_port = i.text.replace('\t', '').replace('\n', '') \
                      + ':' + j.text.replace('\n', '').replace('\t', '')
            ip_list.append(ip_port)

    def run(self):
        for x in range(1, 20):
            self._get_ip(x)
        return get_random_ip()

你可能感兴趣的:(python,https)