0

0

python爬取网站数据保存使用的方法

php中文网

php中文网

发布时间:2016-06-16 08:46:21

|

1240人浏览过

|

来源于php中文网

原创

编码问题
因为涉及到中文,所以必然地涉及到了编码的问题,这一次借这个机会算是彻底搞清楚了。
问题要从文字的编码讲起。原本的英文编码只有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编码下的'中文',并不相同。可以看如下代码:

复制代码 代码如下:

>>> 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

复制代码 代码如下:

#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 改为

复制代码 代码如下:

#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'

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

复制代码 代码如下:

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

这样就可以获得到页面的内容。接下来再用正则匹配去匹配所需要的内容就行了。
但是,真正要做起来,就会有各种各样的细节问题。
登录
这是一个需要登录认证的网站。也不太难,只要导入cookielib和urllib库就行。

SmartB2B行业电子商务
SmartB2B行业电子商务

SmartB2B 是一款基于PHP、MySQL、Smarty的B2B行业电子商务网站管理系统,系统提供了供求模型、企业模型、产品模型、人才招聘模型、资讯模型等模块,适用于想在行业里取得领先地位的企业快速假设B2B网站,可以运行于Linux与Windows等多重服务器环境,安装方便,使用灵活。 系统使用当前流行的PHP语言开发,以MySQL为数据库,采用B/S架构,MVC模式开发。融入了模型化、模板

下载

复制代码 代码如下:

import urllib,urllib2,cookielib
cookiejar = cookielib.CookieJar()
urlOpener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookiejar))

这样就装载进一个cookie,用urlOpener去open登录以后就可以记住信息。
断线重连
如果只是做到上面的程度,不对open进行包装的话,只要网络状况有些起伏,就直接抛出异常,退出整个程序,是个很不好的程序。这个时候,只要对异常进行处理,多试几次就行了:

复制代码 代码如下:

def multi_open(opener,*arg):
    while True:
        retryTimes=20
        while retryTimes>0:
            try:
                return opener.open(*arg)
            except:
                print '.',
                retryTimes-=1

正则匹配
其实正则匹配并不算是一个特别好的方法,因为它的容错性很不好,网页要完全统一。如果有稍微的不统一,就会失败。后来看到说有根据xpath来进行选取的,下次可以尝试一下。
写正则其实是有一定技巧的:
非贪婪匹配。比如这样一个标签:hello,要取出a来,如果写成这样的表达式,就不行了:hello。因为*进行了贪婪匹配。这是要用.?:hello
跨行匹配。实现跨行有一种思路是运用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,需要电脑上安装了软件才行。我采用的是前一种。
基本的读写没有问题。但是数据量一大起来,就有问题了。
内存不够。程序一跑起来,内存占用就一点一点往上涨。后面再查了一下,知道要用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包装了一下

复制代码 代码如下:

#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')

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

复制代码 代码如下:

html_escape_table = {
    "&": "&",
    '"': """,
    "'": "'",
    ">": ">",
    "    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速学教程(入门到精通)
python速学教程(入门到精通)

python怎么学习?python怎么入门?python在哪学?python怎么学才快?不用担心,这里为大家提供了python速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
高德地图升级方法汇总
高德地图升级方法汇总

本专题整合了高德地图升级相关教程,阅读专题下面的文章了解更多详细内容。

65

2026.01.16

全民K歌得高分教程大全
全民K歌得高分教程大全

本专题整合了全民K歌得高分技巧汇总,阅读专题下面的文章了解更多详细内容。

123

2026.01.16

C++ 单元测试与代码质量保障
C++ 单元测试与代码质量保障

本专题系统讲解 C++ 在单元测试与代码质量保障方面的实战方法,包括测试驱动开发理念、Google Test/Google Mock 的使用、测试用例设计、边界条件验证、持续集成中的自动化测试流程,以及常见代码质量问题的发现与修复。通过工程化示例,帮助开发者建立 可测试、可维护、高质量的 C++ 项目体系。

33

2026.01.16

java数据库连接教程大全
java数据库连接教程大全

本专题整合了java数据库连接相关教程,阅读专题下面的文章了解更多详细内容。

39

2026.01.15

Java音频处理教程汇总
Java音频处理教程汇总

本专题整合了java音频处理教程大全,阅读专题下面的文章了解更多详细内容。

19

2026.01.15

windows查看wifi密码教程大全
windows查看wifi密码教程大全

本专题整合了windows查看wifi密码教程大全,阅读专题下面的文章了解更多详细内容。

85

2026.01.15

浏览器缓存清理方法汇总
浏览器缓存清理方法汇总

本专题整合了浏览器缓存清理教程汇总,阅读专题下面的文章了解更多详细内容。

20

2026.01.15

ps图片相关教程汇总
ps图片相关教程汇总

本专题整合了ps图片设置相关教程合集,阅读专题下面的文章了解更多详细内容。

11

2026.01.15

ppt一键生成相关合集
ppt一键生成相关合集

本专题整合了ppt一键生成相关教程汇总,阅读专题下面的的文章了解更多详细内容。

47

2026.01.15

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Vue 教程
Vue 教程

共42课时 | 6.7万人学习

Django 教程
Django 教程

共28课时 | 3.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号