关于起点中文网字体反爬的解决方法——以阅读指数榜为例

一、准备阶段

1.目标网站观察

①采集网页属于静态网页
②网站做了字体反爬,关键信息在html中是乱码
在这里插入图片描述

二、关于起点中文网的字体反爬

1.什么是字体反爬

① 网站采取的一种反爬措施
② 通过自定义字体文件的方式,让前端显示正常,但在html中是乱码
③ 一般自定义的字体文件是随机的,每次请求都会改变。还有一种字体反爬更加变态,在字体文件中还有一次随机,这样子即使获取到字体文件也很难找到字体Unicode与前端显示本文的正确对应关系,这种程度的反爬,我想应该需要通过机器学习来解决,本文并没有对此作答。

2.对起点中文网的字体反爬的解析

①通过对整个html的分析,找到了下载woff字体文件的网址,截取出最重要的部分
在这里插入图片描述
②其中code对应的就是html页面中乱码的十六进制编码,name对应的应该就是数字的英文表达
③看到这里,思路其实就应该清晰了。但这应该算是最简单的字体反爬了,像58同城,还需要再找一组对应,最后结果应该是GlyphOrder标签中的id减去1;猫眼电影应该是最难的,属于我上述的双随机,可能需要KNN算法来解决。

三、代码逻辑

1.通过请求起始页面采集基本信息以及详细页面的url
2.在详细页面中利用re正则表达式解析得到字体下载文件和乱码部分
3.利用fontTools.ttLib解析字体文件得到对应camp
4.配合乱码部分的十六进制编码,得到正确结果
5.scrapy crawl main -o res.csv将结果保存为csv

四、代码部分

main.py

import scrapy
import re
import requests
from fontTools.ttLib import TTFont
from io import BytesIO
import time
from ..items import QidianxiaoshuoItem



class MainSpider(scrapy.Spider):
    name = 'main'
    # allowed_domains = ['main.com']
    # start_urls = ['https://www.qidian.com/rank/readIndex?page=1',
    #               'https://www.qidian.com/rank/readIndex?page=2']

    start_urls = [f'https://www.qidian.com/rank/readIndex?page={i}' for i in range(1,6)]



    def get_font(self,url):
        time.sleep(1)
        response = requests.get(url)
        font = TTFont(BytesIO(response.content))
        cmap = font.getBestCmap()
        font.close()
        return cmap

    def get_encode(self,cmap, values):
        WORD_MAP = {'zero': '0', 'one': '1', 'two': '2', 'three': '3', 'four': '4', 'five': '5', 'six': '6',
                    'seven': '7',
                    'eight': '8', 'nine': '9', 'period': '.'}
        word_count = ''
        list = values.split(';')
        list.pop(-1)
        for value in list:
            value = value[2:]
            key = cmap[int(value)]
            word_count += WORD_MAP[key]
        return word_count

    def get_nums(self,url):
        # 获取当前页面的html
        time.sleep(1)
        response = requests.get(url).text
        pattern = re.compile('</style><span.*?>(.*?)</span>', re.S)
        #  获取当前页面所有被字数字符
        numberlist = re.findall(pattern, response)
        # 获取当前包含字体文件链接的文本
        reg = re.compile('<style>(.*?)\s*</style>', re.S)
        fonturl = re.findall(reg, response)[0]
        # 通过正则获取当前页面字体文件链接
        url = re.search('woff.*?url.*?\'(.+?)\'.*?truetype', fonturl).group(1)
        cmap = self.get_font(url)
        print('cmap:', cmap)
        num_list = []
        for a in numberlist:
            num_list.append(self.get_encode(cmap, a))
        return num_list

    def parse(self, response):
        res = response.xpath('//*[@id="rank-view-list"]/div/ul/li')
        for i in res:
            url = i.css('div:nth-child(1) a::attr(href)').extract_first()
            url = 'https:' + url
            yield scrapy.Request(url,callback = self.parse_one,meta={'url':url})

    def parse_one(self,response):
        book_name = response.css('div.book-info h1 em::text').extract_first()
        author = response.css('a.writer::text').extract_first()
        intro =  response.xpath('/html/body/div/div[6]/div[1]/div[2]/p[2]/text()').extract_first()
        num_list = self.get_nums(response.meta['url'])
        del num_list[1]
        word_num = str(num_list[0]) + response.xpath('/html/body/div/div[6]/div[1]/div[2]/p[3]/cite[1]/text()').extract_first()
        recommend_all = str(num_list[1]) + response.xpath('/html/body/div/div[6]/div[1]/div[2]/p[3]/cite[2]/text()').extract_first()
        recommend_week = str(num_list[2]) + response.xpath('/html/body/div/div[6]/div[1]/div[2]/p[3]/cite[3]/text()').extract_first()

        item = QidianxiaoshuoItem()
        item['book_name'] = book_name
        item['author'] = author
        item['intro'] = intro
        item['word_num'] = word_num
        item['recommend_all'] = recommend_all
        item['recommend_week'] = recommend_week

        yield item

items.py

import scrapy


class QidianxiaoshuoItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    book_name = scrapy.Field()
    author = scrapy.Field()
    intro = scrapy.Field()
    word_num = scrapy.Field()
    recommend_all = scrapy.Field()
    recommend_week = scrapy.Field()
最终结果:

其中一个页面:
在这里插入图片描述
CSV文件:
在这里插入图片描述

五、总结

遇见字体反爬的网页,需要更多的耐心,要在一张张网页源码中找到需要的信息(woff文件下载网址,乱码部分)。

  • 6
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值