网络爬虫在爬取网页时,响应头没有编码信息...如何解决保存在本地的乱码问题?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了网络爬虫在爬取网页时,响应头没有编码信息...如何解决保存在本地的乱码问题?相关的知识,希望对你有一定的参考价值。

你这个问题实际就是浏览器是如何解码的!

    大部分网页在header 都给定了编码方式的,否则浏览器也无法判断是什么编码

    默认使用GBK 的编码,中文网站大部分都是用的这个编码方式,兼容英文

追问

有些网站的header是没有给定编码的,例如http://www.sohu.com/....如果默认GBK的话,网页的编码是UTF-8的就会乱码

追答

你去看源代码的第四行

源代码里面会有

追问

谢谢了...页面的源代码里有编码的,乱码的只是中文,英文还是可以取出来的。。

参考技术A 用python爬取网页的乱码问题解决方法:
一、将获取的网页响应,手动设置响应数据的编码格式
response.encoding = 'utf-8'
二、找到发生乱码所对应的数据,对数据单独进行解码编码
img_name = img_name.encode('iso-8859-1').decode('gbk')
也可以用采集工具进行爬取网页端数据

位图:爬虫URL去重最佳方案

网页爬虫,解析已爬取页面中的网页链接,再爬取这些链接对应网页。而同一网页链接有可能被包含在多个页面中,这就会导致爬虫在爬取的过程中,重复爬取相同的网页。

1如何避免重复爬取?

记录已爬取的网页链接(也就是URL),在爬取一个新的网页之前,我们拿它的链接,在已经爬取的网页链接列表中搜索:

  • 存在,这网页已被爬过
  • 不存在,还没被爬过,可继续去爬

等爬取到这网页后,将这网页的链接添加到已爬取的网页链接列表。

如何记录已爬取的网页链接?
要处理的对象是网页链接URL,需支持:

  • 添加一个URL和查询一个URL
  • 还要求这两个操作执行效率尽可能高

处理上亿网页链接,内存消耗大,存储效率要尽可能高效。散列表、红黑树、跳表这些动态数据结构,都支持快速插入、查找数据,但内存消耗是否满足?

为判重

2 10亿网页链接存储在散列表,需多少内存?

假设一个URL平均64字节,10亿URL=60GB内存。因为散列表须维持较小装载因子,保证不出现过多冲突,导致操作性能下降。且链表法解决冲突时,还会存储链表指针。所以,10亿URL构建成散列表

位图:爬虫URL去重最佳方案_布隆过滤器

可分治,用多台机器(如20台8G内存的机器)存储这10亿网页链接。

但还得考虑,在添加、查询数据的效率及内存消耗,如何优化?

3 散列表中添加、查询时间复杂度是位图:爬虫URL去重最佳方案_数据_02,还咋优化?

时间复杂度不完全代表代码执行时间。因为大O时间复杂度表示法,会忽略常数、系数和低阶,统计对象是语句的频度。不同语句,执行时间不同。时间复杂度只是表示执行时间随数据规模的变化趋势,并不能度量在特定的数据规模下的代码执行时间。

若时间复杂度原来系数是10,现在能够优化将系数降为1,则时间复杂度没有变化情况下,执行效率就提高10倍。

若用基于链表解冲突,散列表存储URL,则查询时,通过哈希函数定位到某链表后,还需依次比对每个链表中的URL。较耗时:

  • 链表中的结点在内存非连续存储,无法一次性加载到CPU缓存,无法很好利用CPU高速缓存,数据访问性能方面遭到打击
  • 链表中的每个数据都是URL,而URL不是简单的数字,是平均长度为64字节的字符串。也就是说,我们要让待判重的URL,跟链表中的每个URL,做字符串匹配。显然,这样一个字符串匹配操作,比起单纯的数字比对,要慢很多。所以,基于这两点,执行效率方面肯定是有优化空间的。

想内存方面有明显节省,就得

4 布隆过滤器(Bloom Filter)

基于位图(BitMap),是对位图的一种改进。

1千万个整数,整数的范围在1到1亿之间。如何快速查找某个整数是否在这1千万个整数?还是可以用散列表。但可使用一种比较“特殊”的散列表:位图。
申请一个大小为1亿、数据类型为布尔类型(true或false)数组。将这1千万个整数作为数组下标,将对应的数组值设置成true。比如,整数5对应下标为5的数组值设置为true,也就是array[5]=true。
查询某个整数K是否在这1千万个整数中的时候,我们只需要将对应的数组值array[K]取出来

  • 等于true,说明1千万整数中包含这个整数
  • 否则,不包含

很多语言中提供的布尔类型,大小1个字节,并不能节省太多内存空间。表示true和false两个值,只需一个二进制位(bit)。

5 如何通过编程语言表示一个二进制位

位运算!可以借助编程语言中提供的数据类型,比如int、long、char等类型,通过位运算,用其中的某个位表示某个数字。

public class BitMap 
private char[] bytes;
private int nbits;

public BitMap(int nbits)
this.nbits = nbits;
this.bytes = new char[nbits/8+1];


public void set(int k)
if (k > nbits) return;
int byteIndex = k / 8;
int bitIndex = k % 8;
bytes[byteIndex] |= (1 << bitIndex);


public boolean get(int k)
if (k > nbits) return false;
int byteIndex = k / 8;
int bitIndex = k % 8;
return (bytes[byteIndex] & (1 << bitIndex)) != 0;

!

位图通过数组下标定位数据,访问效率高。每个数字用一个二进制位来表示,在数字范围不大的情况下,所需要的内存空间节省。

用散列表存储这1千万的数据,数据是32位整型数,即需4个字节,总共至少40MB。若通过位图,数字范围在1到1亿之间,只需要1亿个二进制位,即12MB。

但这有假设:

6 限制:数字所在范围不是很大

如数字范围很大,比如刚刚问题,数字范围不是11亿,而是110亿,则位图大小10亿个二进制位,即120MB。

这时,该布隆过滤器出场,就是为解决这种问题,对位图这种数据结构的改进。

数据个数是1千万,数据范围1~10亿。仍使用一个1亿个二进制大小的位图,然后通过哈希函数,对数字进行处理,让它落在这1到1亿范围内。
如把哈希函数设计成f(x)=x%n:

  • x表示数字
  • n表示位图的大小(1亿),即对数字跟位图的大小进行取模求余

哈希函数会存在冲突的问题啊,一亿零一和1两个数字,经过你刚刚那个取模求余的哈希函数处理之后,最后的结果都是1。这样我就无法区分,位图存储的是1还是一亿零一。

为降低这种冲突概率,当然可设计一个复杂点、随机点哈希函数。

7 哈希函数优化

布隆过滤器怎么处理的?既然一个哈希函数可能会存在冲突,那用多个哈希函数一块儿定位一个数据,是否能降低冲突的概率呢?使用K个哈希函数

  • 对同一个数字进行求哈希值,会得到K个不同哈希值,分别记作位图:爬虫URL去重最佳方案_布隆过滤器_03位图:爬虫URL去重最佳方案_位图_04位图:爬虫URL去重最佳方案_数据_05,…,位图:爬虫URL去重最佳方案_数据_06
  • 把这K个数字作为位图中的下标,将对应的BitMap[位图:爬虫URL去重最佳方案_布隆过滤器_03],BitMap[位图:爬虫URL去重最佳方案_位图_04],BitMap[位图:爬虫URL去重最佳方案_数据_05],…,BitMap[位图:爬虫URL去重最佳方案_数据_06]都设成true,即用K个二进制位,表示一个数字。

查询某数字是否存在时,用同样K个哈希函数,对这个数字求哈希值,分别得到位图:爬虫URL去重最佳方案_布隆过滤器_11位图:爬虫URL去重最佳方案_布隆过滤器_12位图:爬虫URL去重最佳方案_数据_13,…,位图:爬虫URL去重最佳方案_位图_14
看这K个哈希值,对应位图中的数值是否都为true:

  • 都是true,则说明,这个数字存在
  • 有任一不为true,那就说明这个数字不存在

两个不同数字,经过一个哈希函数处理之后,可能会产生相同的哈希值。但是经过K个哈希函数处理之后,K个哈希值都相同的概率就非常低了。

但这种处理方式容易

8 误判

只会对存在的情况有误判:

  • 若某数字经布隆过滤器判断不存在,则说明该数字真不存在,不会误判
  • 若某数字经布隆过滤器判断存在,这时才可能误判,有可能并不存在
    只要调整哈希函数的个数、位图大小跟要存储数字的个数之间的比例,那就可以将这种误判的概率降到非常低。

尽管布隆过滤器会存在误判,但并不影响发挥作用。很多场景对误判有一定容忍度。如爬虫判重这个问题,即便一个没有被爬取过的网页,被误判为已经被爬取,对于搜索引擎来说,也并不是什么大事情,是可以容忍的。

用布隆过滤器来记录已经爬取过的网页链接,假设需要判重的网页有10亿,那我们可以用一个10倍大小的位图来存储,也就是100亿个二进制位,换算成字节,那就是大约1.2GB。之前我们用散列表判重,需要至少100GB的空间。相比来讲,布隆过滤器在存储空间的消耗上,降低了非常多。

9 布隆过滤器执行效率比散列表高效吗

布隆过滤器用多个哈希函数对同一个网页链接进行处理,CPU只需要将网页链接从内存中读取一次,进行多次哈希计算,理论上讲这组操作是CPU密集型。

散列表需读取散列冲突拉链的多个网页链接,分别跟待判重的网页链接,进行字符串匹配。这个操作涉及很多内存数据的读取,是内存密集型。

CPU计算可能是要比内存访问更快速的,理论上讲,布隆过滤器判重更快速。

10 总结

布隆过滤器非常适合这种不需要100%准确的、允许存在小概率误判的大规模判重场景。除了爬虫网页去重这个例子,还有比如统计一个大型网站的每天的UV数,也就是每天有多少用户访问了网站,我们就可以使用布隆过滤器,对重复访问的用户,进行去重。

布隆过滤器的误判率,主要跟哈希函数的个数、位图的大小有关。当我们往布隆过滤器中不停地加入数据之后,位图中不是true的位置就越来越少了,误判率就越来越高了。所以,对于无法事先知道要判重的数据个数的情况,我们需要支持自动扩容的功能。

当布隆过滤器中,数据个数与位图大小的比例超过某个阈值的时候,我们就重新申请一个新的位图。后面来的新数据,会被放置到新的位图中。但是,如果我们要判断某个数据是否在布隆过滤器中已经存在,我们就需要查看多个位图,相应的执行效率就降低了一些。

位图、布隆过滤器应用如此广泛,很多编程语言都已经实现了。如Java中的BitSet类就是一个位图,Redis也提供了BitMap位图类,Google的Guava工具包提供了BloomFilter布隆过滤器的实现。

以上是关于网络爬虫在爬取网页时,响应头没有编码信息...如何解决保存在本地的乱码问题?的主要内容,如果未能解决你的问题,请参考以下文章

位图:爬虫URL去重最佳方案

自己写了一个爬虫,求教如何在网页上爬取动态加载的信息。

网络爬虫之selenium(综述)

位图:爬虫URL去重最佳方案

位图:爬虫URL去重最佳方案

Python爬虫系列:判断目标网页编码的几种方法