频道栏目
首页 > 资讯 > Python > 正文

python爬虫(中)--数据建模与保存(入库)

16-08-27        来源:[db:作者]  
收藏   我要投稿

前言

前面,讲的是提取出来的数据保存进一个extracted_data,再保存进extracted_data_,变成一个list包含list的情况,当然你只提取一项,那就没有必要这么做了,可是我的项目中要求可能要提取十几二十项,我为了后面入库方便,所以前面做了这么一个工作。

到提取为止,基本爬虫差不多就完成了,什么是基本爬虫,基本爬虫=请求+提取+保存,而不考虑一些针对反反爬的策略制定等情况。现在我们请求函数有了,提取函数有了,现在就差一个保存函数了。我们这里选择的是保存到MySQL数据库中,当然你也可以保存成为.html等格式的文本,我们保存成数据库是为了后面方便使用考虑.不过在保存之前请认真考虑一下你的数据建模问题.

数据建模不是单独存在的,更多的是为了入库而准备的,根据自己需要的数据有多少种类和每种数据的特征,甚至还要考虑到大数据量存储的问题和未来要提数据的场景,来设计数据库中的表,这里工作很多,很考验数据结构的基本功,换言之是一个很费脑,而且要发挥想象力的步骤,所以做这一步的时候最好不要在昏昏欲睡的时候做,要在头脑清晰最为适宜.


保存到文件

简单提提文件保存,先上个保存成HTML文件为例子:

#! /usr/bin/env python
#encoding:utf-8

from os import makedirs
from os.path import exists
import codecs
import json

def save_As_HTMLFile(directory,extracted_data_):

    if  isinstance(extracted_data_,list) :
        print 'the sourceCode you put in save_As_HTMLFile() is a list!!Here will transform list into string\n'
        #json用在列表/字典的转unicode上
        extracted_data_=json.dumps(extracted_data_,encoding="utf-8",ensure_ascii=False)

    if not exists(directory):
        makedirs(directory)
        print 'Finish Making Directory  ',directory

    filename=directory+'extracted_data_from_phantomjs.html'

    f=codecs.open(filename,'wb')#wb是以二进制写模式打开(打开前文件会被清空)更多的看下面注意第四点
    print 'saving data...\n'
    f.write(extracted_data_)#write()里面要放的是str/buffer,不能是JPEG或者其他奇奇怪怪的东西
    print 'Saved.\n'    
    f.close()

    return

要注意一下几点:

if isinstance(extracted_data_,list)这里是为了应付之前extracted_data_传进来是list的情况,如果这个if判断到传进来的extracted_data_那么就返回True,然后用json模块中的json.dumps()的方法直接转换成字符串格式,unicode编码的文本~

codecs模块的应用,知道python的朋友应该知道其实不用codecs这个模块,把 f=codecs.open(filename,’wb’)换成f=open(filename,’wb’)也是一样的效果,那么为什么要用codecs.open的方法呢?

答:先来讲讲历史,最早的时候只有open(),可是你也知道python2的编码问题是有多么蛋疼(所以学习python得善用type()方法),所以就有了codecs.open()。而io.open(),其实是因为python2的open()是由File模块提供的,而python3的open()是由io模块提供的。然后,python2.6引入python3的特性,叫io.open(),以区别于原来由File模块提供的open()。python3建议直接用open(),而python2.*用codecs.open()特别是有中文的情况下,如果希望代码能同时是兼容python2和3,就直接用codecs.open(),这就是为什么我们在上面代码里面用codecs.open()的原因,主要考虑compatible的问题。

codecs.open()和open()[指io.open()]使用上的区别?

Answer : io.open() doesn’t always produce unicode strings,if you pass ‘rb’ as the mode,the file is opened in a binary mode,not text mode,and read() method will return bytes.
codecs.open() always open the file in binary mode.

open(),io.open()还是codecs.open()中模式名称用法是一样的,都可以直接参考open()参数mode那一项的设置——详细请见博客

P.s.既然讲到了python的文件保存,那就顺便说说python中获取用户输入的方法——raw_input()和input():

raw_input():任何都可以输入,无论输入什么都会当成字符串
input() : 希望用户的输入的是一个合法python表达式,比如你输入字符串时必须使用引号(单双引号都可以)引起来,不然就报SyntaxError

本质上input()还是调用的是raw_input()实现的,只是调用完raw_input()后再调用eval()

※除非是对input()有需求,不然一般情况推荐raw_input()


保存到MySQL

【思考1:自然分层设计和查询效率之间的取舍?】
【思考2:一般来说所有程序都是直接接到db,不需要db来区分,一个项目就一个db就好了?】

首先,为了更加具体而且没有那么枯燥,我先放张图——MySQL层次:
这里写图片描述

python中通过MySQLdb 这个模块完成与数据库链接等操作,然而有些系统并没有装这个模块,那么就要sudo pip install MySQL-python安装之。

这里存入MySQL数据库实现起来其实并不复杂,而且都是套路:

def save_As_Mysql(extracted_data_):

    print 'connecting to mysql....'
    #套路一.链接数据库,别忘记开启MySQL服务
    conn=MySQLdb.connect(

        host='localhost',
        port=3306,
        user='root',
        passwd='填你的Mysql数据库密码',
        db='填你要接到的db名字',
        charset='utf8'
    )#charset指定连接编码格式

    #套路二.创建游标
    cur=conn.cursor()

    #测试用
    # sql_dropTable='drop table if exists '+str(from_city)+'到'+str(to_city)
    # cur.execute(sql_dropTable)
    # print '删除原表成功'

    #套路三.创建表
    sql_newTable='create table if not exists table_name(Field1 varchar(10),Field2 varchar(20))'
    cur.execute(sql_newTable)
    print '新表建立成功'

    #套路四.往表中插入数据
    sql_insert='insert into table_name values(%s,%s)'
    cur.executemany(sql_insert,extracted_data_)#extracted_data_列表中列表
    #cur.fetchall()
    conn.commit()
    cur.close()
    conn.close()

更加准确来说其实MySQLdb要用到的只是套路一的链接数据库,套路二的创建游标,套路三的execute(sql,list/tuple)单次执行sql语句,套路四executemany(sql,[list,list,…]/[tuple,tuple,…])多次执行sql语句,conn.commit()把已经有的修改动作真实提交,cur.close()游标关闭,conn.close()链接关闭.
这些东西都是死的,我们真正起作用的地方是SQL语句的设计编写,MySQLdb只是负责execute或者executemany罢了

说明几点:
①MySQL中的字段类型之字符串
1.char(n):
定长,char不管实际value长度,都会占用固定的几个字符空间,速度快,而且处理字符串尾部空格,超过设置长度n的部分会截断
2.varchar(n):
变长,速度慢,不处理字符串尾部空格(UpperLimit:65535字节),超过设置长度n的部分会截断
3.text:
速度慢,不处理字符串尾部空格,而且会用额外空间去存放数据长度这个量

char(n)和varchar(n)中的n代表字符个数,并非代表字节个数,当使用中文(utf-8)时,意味着可以插入m个汉字,但是实际上会占用3m个字节,当然如果只是英文的话就是m个字母,实际上占用m个字节
当超过255长度后,varchar和text没有区别,只需要考虑两者类型和特性。

②SQL语句是str格式的
③mysql默认是lantin1编码,因为是由瑞典开发的.要改变的话,在mysql的shell下输入 set names utf8

有个花了我很久去解决的Error,我一定要在这里说出来- -#:
ProgrammingError:not all arguments converted during string formatting
TypeError:not all arguments converted during string formatting

解决办法:第一,请仔细check一下你的SQL语句有木有写错!第二,cur.execute()和cur.executemany()的区别和用法有没有弄清楚!

很不幸,我就是死在第二点,所以我要特别地在这里说说这两种的区别:

①cur.execute(sql,list/tuple),这里的list不能是那种list包括list的状况,只能单纯的一个list,比如[“16-8-26”,”16年08月30日”]这样的,而不是[[“16-8-26”,”16年08月30日”],[“16-8-26”,”16年08月31日”]]这样的列表嵌套列表,不然以我被它折腾了我一天的经验来看,分分钟给你报not all arguments converted during string formatting 。

②cur.executemany(sql,list-list/list-tuple),所谓list-list就是[[“16-8-26”,”16年08月30日”],[“16-8-26”,”16年08月31日”]]这种了,第一次就会取出list中的第一个list——[“16-8-26”,”16年08月30日”],过来就像execute时候一样,把这个取出的list中每一项取出放到前面的%s(占位符)位置,弄完了就去最外层list的第二个list,以此类推….

相关TAG标签
上一篇:消息中间件kafka(0.9以及0.10版本)学习及实践
下一篇:java学习之路 之 IO流
相关文章
图文推荐

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站