编码问题

因为涉及到中文,所以必然地涉及到了编码的问题,这一次借这个机会算是彻底搞清楚了。

问题要从文字的编码讲起。原本的英文编码只有0~255,刚好是8位1个字节。为了表示各种不同的语言,自然要进行扩充。中文的话有GB系列。可能还听说过Unicode和UTF-8,那么,它们之间是什么关系呢?

Unicode是一种编码方案,又称万国码,可见其包含之广。但是具体存储到计算机上,并不用这种编码,可以说它起着一个中间人的作用。你可以再把Unicode编码(encode)为UTF-8,或者GB,再存储到计算机上。UTF-8或者GB也可以进行解码(decode)还原为Unicode。

在python中Unicode是一类对象,表现为以u打头的,比如u'中文',而string又是一类对象,是在具体编码方式下的实际存在计算机上的字符串。比如utf-8编码下的'中文'和gbk编码下的'中文',并不相同。可以看如下代码:

?

1

2

3

4

5

6

7

8

9

>>> str=u'中文'

>>> str1=str.encode('utf8')

>>> str2=str.encode('gbk')

>>> print repr(str)

u'\u4e2d\u6587'

>>> print repr(str1)

'\xe4\xb8\xad\xe6\x96\x87'

>>> print repr(str2)

'\xd6\xd0\xce\xc4'

可以看到,其实存储在计算机中的只是这样的编码,而不是一个一个的汉字,在print的时候要知道当时是用的什么样的编码方式,才能正确的print出来。有一个说法提得很好,python中的Unicode才是真正的字符串,而string是字节串

文件编码

既然有不同的编码,那么如果在代码文件中直接写string的话,那么它到底是哪一种编码呢?这个就是由文件的编码所决定的。文件总是以一定的编码方式保存的。而python文件可以写上coding的声明语句,用来说明这个文件是用什么编码方式保存的。如果声明的编码方式和实际保存的编码方式不一致就会出现异常。可以见下面例子: 以utf-8保存的文件声明为gbk

?

1

2

3

4

5

6

7

8

9

#coding:gbk

str=u'汉'

str1=str.encode('utf8')

str2=str.encode('gbk')

str3='汉'

print repr(str)

print repr(str1)

print repr(str2)

print repr(str3)

提示错误 File "test.py", line 1 SyntaxError: Non-ASCII character '\xe6' in file test.py on line 1, but no encodi ng declared; see http://www.python.org/peps/pep-0263.html for details 改为

?

1

2

3

4

5

6

7

8

9

#coding:utf8

str=u'汉'

str1=str.encode('utf8')

str2=str.encode('gbk')

str3='汉'

print repr(str)

print repr(str1)

print repr(str2)

print repr(str3)

输出正常结果 u'\u6c49' '\xe6\xb1\x89' '\xba\xba' '\xe6\xb1\x89'

更多内容可参见这篇文章http://www.cnblogs.com/huxi/archive/2010/12/05/1897271.html

基本方法

其实用python爬取网页很简单,只有简单的几句话

?

1

2

import urllib2

page=urllib2.urlopen('url').read()

这样就可以获得到页面的内容。接下来再用正则匹配去匹配所需要的内容就行了。

但是,真正要做起来,就会有各种各样的细节问题。

登录

这是一个需要登录认证的网站。也不太难,只要导入cookielib和urllib库就行。

?

1

2

3

import urllib,urllib2,cookielib

cookiejar = cookielib.CookieJar()

urlOpener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookiejar))

这样就装载进一个cookie,用urlOpener去open登录以后就可以记住信息。

断线重连

如果只是做到上面的程度,不对open进行包装的话,只要网络状况有些起伏,就直接抛出异常,退出整个程序,是个很不好的程序。这个时候,只要对异常进行处理,多试几次就行了:

?

1

2

3

4

5

6

7

8

9

def multi_open(opener,*arg):

    while True:

        retryTimes=20

        while retryTimes>0:

            try:

                return opener.open(*arg)

            except:

                print '.',

                retryTimes-=1

正则匹配

其实正则匹配并不算是一个特别好的方法,因为它的容错性很不好,网页要完全统一。如果有稍微的不统一,就会失败。后来看到说有根据xpath来进行选取的,下次可以尝试一下。

写正则其实是有一定技巧的:

  • 非贪婪匹配。比如这样一个标签:<span class='a'>hello</span>,要取出a来,如果写成这样的表达式,就不行了:<span class=.*>hello</span>。因为*进行了贪婪匹配。这是要用.?:<span class=.?>hello</span>。
  • 跨行匹配。实现跨行有一种思路是运用DOTALL标志位,这样.就会匹配到换行。但是这样一来,整个匹配过程就会变得很慢。本来的匹配是以行为单位的。整个过程最多就是O(nc2),n是行数,c是平均列数。现在极有可能变为O((nc)2)。我的实现方案是运用\n来匹配换行,这样可以明确指出匹配最多跨跃多少行。比如:abc\s*\n\s*def,就指出查找的是隔一行的。(.\n)?就可以指定是匹配尽可能少的行。
  • 这里其实还要注意一个点。有的行末是带有\r的。也就是说一行是以\r\n结尾的。当初不知道这一点,正则就调试了很久。现在直接用\s,表示行末空格和\r。
  • 无捕获分组。为了不对捕获的分组造成影响,上面的(.\n)可以改为(?:.\n),这样捕获分组时,就会忽略它。
  • 单括号要进行转义。因为单括号在正则里是用来表示分组的,所以为了匹配单括号就进行转义。正则字符串最好用的是带有r前缀的字符串,如果不是的话,则要对\再进行转义。
  • 快速正则。写了那么多模式,也总结出一规律出来。先把要匹配的字符相关的段落拿出来。要匹配的东西用(.?)代替。把换行\n替换为字符串\s\n\s*,再去掉行首行末的空格。整个过程在vim中可以很快就写好。

Excel操作

这次的数据是放进Excel的。到后面才意识到如果放进数据库的话,可能就没有那么多事了。但是已经写到一半,难以回头了。

搜索Excel,可以得出几个方案来,一个是用xlrt/xlwt库,这个不管电脑上是否安装了Excel,都可以运行,但只能是xls格式的。还有一个是直接包装了com,需要电脑上安装了软件才行。我采用的是前一种。如果大家对Python感兴趣的话,可以加一下我们的学习交流抠抠群哦:649825285,免费领取一套学习资料和视频课程哟~

基本的读写没有问题。但是数据量一大起来,就有问题了。

  • 内存不够。程序一跑起来,内存占用就一点一点往上涨。后面再查了一下,知道要用flush_row_data。但是还是会出错。一看内存占用,没有什么问题,一直很平稳。但最后还是会出现memory error。这真是见鬼了。又是反复地查, 反复地运行。一点结果都没有。要命的是bug只在数据量大起来才出现,而等数据量大起来往往要好几个小时,这debug的成本实在是太高了。一个偶然的机会,突然发现内存占用,虽然总体平稳,但是会规律性的出现小的高涨,而这规律性,会不会和flush_row_data,有关。一直疑惑的是data被flush到了哪里。原来xlwt的作法是很蛋疼的作法。把数据存在内存里,或者flush到一个temp,到save的时候,再一次性写入。而问题正出在这一次性写入,内存猛涨。那我要flush_row_data何用?为什么不一开始就flush进要写入的地方。
  • 行数限制。这个是xls格式本身决定的,最多行数只能是65536。而且数据一大,文件打开也不方便。

结合以上两点,最终采取了这么一个策略,如果行数是1000的倍数,进行一次flush,如果行数超过65536,新开一个sheet,如果超过3个sheet,则新建一个文件。为了方便,把xlwt包装了一下

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

#coding:utf-8#

import xlwt

class XLS:

    '''a class wrap the xlwt'''

    MAX_ROW=65536

    MAX_SHEET_NUM=3

    def __init__(self,name,captionList,typeList,encoding='utf8',flushBound=1000):

        self.name=name

        self.captionList=captionList[:]

        self.typeList=typeList[:]

        self.workbookIndex=1

        self.encoding=encoding

        self.wb=xlwt.Workbook(encoding=self.encoding)

        self.sheetIndex=1

        self.__addSheet()

        self.flushBound=flushBound

    

    def __addSheet(self):

        if self.sheetIndex != 1:

            self.wb.save(self.name+str(self.workbookIndex)+'.xls')

        if self.sheetIndex>XLS.MAX_SHEET_NUM:

            self.workbookIndex+=1

            self.wb=xlwt.Workbook(encoding=self.encoding)

            self.sheetIndex=1

        self.sheet=self.wb.add_sheet(self.name.encode(self.encoding)+str(self.sheetIndex))

        for i in range(len(self.captionList)):

            self.sheet.write(0,i,self.captionList[i])

        self.row=1

    def write(self,data):

        if self.row>=XLS.MAX_ROW:

            self.sheetIndex += 1

            self.__addSheet()

        for i in range(len(data)):

            if self.typeList[i]=="num":

                try:

                    self.sheet.write(self.row,i,float(data[i]))

                except ValueError:

                    pass

            else:

                self.sheet.write(self.row,i,data[i])

        if self.row % self.flushBound == 0:

            self.sheet.flush_row_data()

        self.row+=1

    def save(self):

        self.wb.save(self.name+str(self.workbookIndex)+'.xls')

转换网页特殊字符

由于网页也有自己独特的转义字符,在进行正则匹配的时候就有些麻烦。在官方文档中查到一个用字典替换的方案,私以为不错,拿来做了一些扩充。其中有一些是为保持正则的正确性。

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

html_escape_table = {

    "&": "&amp;",

    '"': "&quot;",

    "'": "&apos;",

    ">": "&gt;",

    "<": "&lt;",

    u"·":"·",

    u"°":"°",

    #regular expression

    ".":r"\.",

    "^":r"\^",

    "$":r"\$",

    "{":r"\{",

    "}":r"\}",

    "\\":r"\\",

    "|":r"\|",

    "(":r"\(",

    ")":r"\)",

    "+":r"\+",

    "*":r"\*",

    "?":r"\?",

}

def html_escape(text):

    """Produce entities within text."""

    tmp="".join(html_escape_table.get(c,c) for c in text)

    return tmp.encode("utf-8")

得出的经验差不多就是这些了。不过最后写出来的程序自已也不忍再看。风格很不好。一开始想着先写着试试。然后试着试着就不想改了。

教你用python爬取网站数据相关推荐

  1. 如何利用python爬取网站数据

    Python是一种非常适合用于网络爬虫的编程语言,以下是Python爬取网站数据的步骤: 1. 确定目标网站和所需数据:首先要找到需要爬取数据的网站,确定你需要爬取的数据是哪一部分. 2. 发送请求: ...

  2. 使用python爬取网站数据并写入到excel中

    文章目录 前言 一.使用python爬取网上数据并写入到excel中 例子一: 例子二: 二.工具类 总结 前言 记录一下使用python将网页上的数据写入到excel中 一.使用python爬取网上 ...

  3. python能爬取网站后台数据_如何利用Python爬取网站数据?

    1.基本方法 其实用python爬取网页很简单,只有简单的几句话 这样就可以获得到页面的内容.接下来再用正则匹配去匹配所需要的内容就行了.但是,真正要做起来,就会有各种各样的细节问题. 2.登录 这是 ...

  4. Python爬取网站数据

    编码问题 因为涉及到中文,所以必然地涉及到了编码的问题,这一次借这个机会算是彻底搞清楚了. 问题要从文字的编码讲起.原本的英文编码只有0~255,刚好是8位1个字节.为了表示各种不同的语言,自然要进行 ...

  5. 今天教你用 Python 爬取网站的指南

    通过基本的 Python 工具获得爬取完整 HTML 网站的实践经验. (本文字数:11235,阅读时长大约:14 分钟) 有很多很棒的书可以帮助你学习 Python ,但是谁真正读了这那些大部头呢? ...

  6. 和我的清华室友经过20天得出的python爬取网站数据!!!

    编码问题 因为涉及到中文,所以必然地涉及到了编码的问题,这一次借这个机会算是彻底搞清楚了. 问题要从文字的编码讲起.原本的英文编码只有0~255,刚好是8位1个字节.为了表示各种不同的语言,自然要进行 ...

  7. 如何利用Python爬取网站数据?

    1.基本方法 其实用python爬取网页很简单,只有简单的几句话: 这样就可以获得到页面的内容.接下来再用正则匹配去匹配所需要的内容就行了.但是,真正要做起来,就会有各种各样的细节问题. 2.登录 这 ...

  8. #python学习笔记#使用python爬取网站数据并保存到数据库

    上篇说到如何使用python通过提取网页元素抓取网站数据并导出到excel中,今天就来说说如何通过获取json爬取数据并且保存到mysql数据库中. 本文主要涉及到三个知识点: 1.通过抓包工具获取网 ...

  9. python爬取网站数据(含代码和讲解)

    提示:本次爬取是利用xpath进行,按文章的顺序走就OK的: 文章目录 前言 一.数据采集的准备 1.观察url规律 2.设定爬取位置和路径(xpath) 二.数据采集 1. 建立存放数据的dataf ...

最新文章

  1. Redis 笔记(07)— sorted set 类型(添加、删除有序集合元素、获取分数范围内成员、按score排序、返回集合元素个数)
  2. 在Windows10上安装ROS并测试
  3. Twisted 介绍 及TCP广播系统实例
  4. 省掉bean自定义spring mvc注解注入json值
  5. 做到我这样,你也能拿到京东Offer
  6. java 学习基础知识点拾遗 导航页
  7. 浏览器屏蔽flash视频广告
  8. c语言追踪机械腿位置,基于传感器的下肢可穿戴机械腿软件系统设计与实现
  9. ks线切割编程系统3.13完整版-2次加密注册码
  10. 开心消消乐简单的逆向破解过程
  11. sql排序,null排在最前/最后----mysql排序之if(isnull(字段名),0,1),fild 或者 if(isnull(字段名),1,0),fild
  12. 华为研发小仙女自述:我和开发的“撕逼”日常
  13. 开关二极管IN4148
  14. 论文细读:HOLMES:Real-time APT Detection through Correlation of Suspicious Information Flows
  15. echo “c“ > /proc/sysrq-trigger 让linux系统崩溃之后的恢复办法
  16. phpini修改时间无效_php配置文件改变为什么没有生效?
  17. 英国Hostinger免费空间申请和使用教程
  18. node.js服务端笔记文档学会写接口,学习分类:path、包、模块化、fs、express、中间件、jwt、开发模式、cors。
  19. pip如何适应多版本的python
  20. android rootfs_android.img,进度栏和Rootfsimg自动更新还应该解决了一些兼容性问题

热门文章

  1. SaaS 转化 3 步曲:让用户变客户
  2. 考研复习——时间安排小结
  3. json php 数组读写,PHP如何将数据写入JSON?
  4. 2016年《大数据》杂志调查问卷
  5. 【Java】I/O流体系中流的分类
  6. Jstatd命令(Java Statistics Monitoring Daemon)
  7. win7装ORACLE提示操作系统未验证或 Service Pack 未执行
  8. 013年十大急需的热门IT技能
  9. RHEL4- ssh服务(二)ssh服务器的配置和启动
  10. 实现页面内多个表格在滚动时,表头浮动的效果(是同时多个表格哟)