码迷,mamicode.com
首页 > 编程语言 > 详细

python 爬虫3 新浪微博 爬虫 实战

时间:2015-08-30 17:32:23      阅读:809      评论:0      收藏:0      [点我收藏+]

标签:python   github   新浪微博   爬虫   源代码   

这次的项目 和文件都放到了 github 上 https://github.com/poiu1235/weibo-catch:

有兴趣的可以follow一下,或者点个赞咯


我这里采用的深度挖掘的方式:没有设定爬取的边界(这个以后是要考虑的)

大致的思路是,用自己的 账号登陆后,获取自己的微博列表和朋友列表。

然后根据朋友列表然后在爬取对方的微博列表和朋友列表。这样不断的深度挖掘和遍历的过程

过程中我采用了mysql 数据库进行存储,后面会加入mongodb 数据库进行存储。


先补充一点linux的知识:系统里那些文件夹都按照什么功能分类的:


/bin 
  bin是binary的缩写。这个目录沿袭了UNIX系统的结构,存放着使用者最经常使用的命令。例如cp、ls、cat,等等。 
/boot 
  这里存放的是启动Linux时使用的一些核心文件。 
/dev 
  dev是device(设备)的缩写。这个目录下是所有Linux的外部设备,其功能类似DOS下的.sys和Win下的.vxd。在Linux中设备和文件是用同种方法访问的。例如:/dev/hda代表第一个物理IDE硬盘。 
/etc 
  这个目录用来存放系统管理所需要的配置文件和子目录。 
/home 
  用户的主目录,比如说有个用户叫wang,那他的主目录就是/home/wang也可以用~wang表示。 
/lib 
  这个目录里存放着系统最基本的动态链接共享库,其作用类似于Windows里的.dll文件。几乎所有的应用程序都须要用到这些共享库。 
/lost+found 
这个目录平时是空的,当系统不正常关机后,这里就成了一些无家可归的文件的避难所。对了,有点类似于DOS下的.chk文件。 
/mnt 
 这个目录是空的,系统提供这个目录是让用户临时挂载别的文件系统。 
/proc 
  这个目录是一个虚拟的目录,它是系统内存的映射,我们可以通过直接访问这个目录来获取系统信息。也就是说,这个目录的内容不在硬盘上而是在内存里。 
/root 
  系统管理员(也叫超级用户)的主目录。作为系统的拥有者,总要有些特权啊!比如单独拥有一个目录。 
/sbin 
  s就是Super User的意思,也就是说这里存放的是系统管理员使用的管理程序。
/tmp 
  这个目录不用说,一定是用来存放一些临时文件的地方了。 
/usr 
  这是最庞大的目录,我们要用到的应用程序和文件几乎都存放在这个目录下。其中包含以下子目录; 
/usr/X11R6 
  存放X-Window的目录; 
/usr/bin 
  存放着许多应用程序; 
/usr/sbin 
  给超级用户使用的一些管理程序就放在这里; 
/usr/doc 
  这是Linux文档的大本营; 
/usr/include 
  Linux下开发和编译应用程序需要的头文件,在这里查找; 
/usr/lib 
  存放一些常用的动态链接共享库和静态档案库; 
/usr/local 
 这是提供给一般用户的/usr目录,在这里安装软件最适合; 
/usr/man 
  man在Linux中是帮助的同义词,这里就是帮助文档的存放目录; 
/usr/src 
  Linux开放的源代码就存在这个目录,爱好者们别放过哦! 
/var 
  这个目录中存放着那些不断在扩充着的东西,为了保持/usr的相对稳定,那些经常被修改的目录可以放在这个目录下,实际上许多系统管理员都是这样干的。顺带说一下系统的日志文件就在/var/log目录中。

爬取微博规则

整个过程我想要爬取两个方面的内容。

一个是微博内容

另一个是朋友(他关注的人,主动,兴趣强烈)关系而不是(关注他的人,粉丝,被动,干扰太多)

爬取下来的话我也打算存在两个地方,一个是mysql 关系数据库里,另一个是以文本文件的形式存到系统

mysql 用的是之前hadoop 系统 里namenode 节点 里的mysql ,ip是192.168.1.113

因为默认mysql 安装时,是绑定本地ip的,就是只限定本地访问,单机访问

所以要更改配置

sudo nano /etc/mysql/my.cnf找到bind-address = 127.0.0.1
注释掉这行,如:#bind-address = 127.0.0.1

允许任意IP访问;
这样注释以后,本地访问就不能省略了 必须用-h 命令 指定主机地址

重启 MySQL:sudo /etc/init.d/mysql restart
授权用户能进行远程连接
   grant all privileges on *.* to root@"%" identified by "password" with grant option;
   flush privileges;
   第一行命令解释如下,*.*:第一个*代表数据库名;第二个*代表表名。这里的意思是所有数据库里的所有表都授权给用户。root:授予root账号。“%”:表示授权的用户IP可以指定,这里代表任意的IP地址都能访问MySQL数据库。“password”:分配账号对应的密码,这里密码自己替换成你的mysql root帐号密码。
   第二行命令是刷新权限信息,也即是让我们所作的设置马上生效。

登陆mysql 用 mysql -uroot -p
如果是登陆其他机器上的mysql 用 mysql -uroot -h192.168.1.113 -p 

#查看编码方式

show variables like "character%";
#修改编码方式

sudo service mysql stop  

sudo nano /etc/mysql/my.cnf  
#在文件内的[mysqld]下增加如下两行设置:
character_set_server=utf8  
init_connect=‘SET NAMES utf8‘ 
sudo service mysql start

数据库字符集和 服务器字符集就会变成utf-8 了,就可以写中文了

这里我在ubuntu上安装的mysql 辅助工具是workbench  还有heidisql  网上不推荐navicat 虽然我在windows 下用navicat 最顺手

默认的mysql目录下data里面的‘mysql‘这个schema没有在workbench里面看到吧?
点击菜单-Edit->Preferences里面的SQL Editor,然后把"show Data Dicrionaries and Internal Schemas"前面的复选框给勾上,

再回过去刷新或者重新连接,就会出现了
接下来就是一些建库,建表的工作了




execute中要使用对等格式 如 name(char), age(int),此时语句为: cur.execute("insert into db.table(name,age) values(%s,%d)" % (v[name], v[age]) ,v[name]为字符,v[age]为int
executemany中不需要强制格式,同上表语句为:
values=[("zhan",23),("li",33)]
executemany("insert into db.tables(name,age) values(%s,%s)",values)

具体原因我也不清楚,只能说是走过的坑吧,executemany一般只用%s即可,用其它的可能会报错。插入后的值是正常的(貌似是自动匹配列属性?),有大侠知道的也望指教

注意: cursor.execute()可以接受一个参数,也可以接受两个参数:
(1) cursor.execute("insert into resource(cid,name) values(%s, %s)" , (12,name) );
    这种格式是接受两个参数,MySQLdb会自动替你对字符串进行转义和加引号,不必再自己进行转义,执行完     此语句之后,resource表中多了一条记录: 12   \
(2) cursor.execute("insert into resource(cid,name) values(%s, %s)" % (12,name) );
    这种格式是利用python的字符串格式化自己生成一个query,也就是传给execute一个参数,此时必须自己对     字符串转义和增加引号,即上边的语句是错误的,应该修改为:
    name = MySQLdb.escape_string(name);
    cursor.execute("insert into resource(cid,name) values(%s, ‘%s‘)" % (12,name) );
    这样插入的记录才和(1)一样:12   \



create database weibocatch;

use weibocatch;
/*汉字和一个字母都是一个字符*/
create table w_user(
wid char(10) primary key,
wname varchar(100) not null,
recon tinyint(1), #0表示未认证,1表示已认证
color tinyint(1) default null,#0表示黄v(个人),1表示蓝v(企业)
flag tinyint(2) default 0, #0表示未被爬取,1表示爬取成功,2表示爬取失败
inserttime timestamp default NOW()
);

create table w_error(
wid char(10),
exception varchar(2000)
);

create table w_relation(
wid char(10),
wfriendid char(10)
);

create table w_conn(
weiboid char(32),
tag tinyint(1),/*判断是转发的微博还是原创的微博,1是转发,0是原创*/
atid char(10),
atname varchar(100),
primary key (weiboid,tag)
);
/*Date或DateTime类型是不能使用函数作为默认值的,所以改用timestamp类型*/
create table w_content(
weiboid char(32) primary key,
wid char(10),
content varchar(1000),
url varchar(1000),
map varchar(200),
label varchar(100),
intime varchar(100),
picid int,
wfrom varchar(100),
inserttime timestamp default NOW()
);
/*Date或DateTime类型是不能使用函数作为默认值的,所以改用timestamp类型*/
create table w_transfer(
weiboid char(32) primary key,
wid char(10),
transferid char(10),
content varchar(1000),
remark varchar(1000),
label varchar(100),
url varchar(1000),
intime varchar(100),
picid int,
wfrom varchar(100),
inserttime timestamp default NOW()
);

create table pic_reg(
id int primary key auto_increment,
path varchar(3000),
label1 varchar(100),
label2 varchar(100),
label3 varchar(100),
title varchar(9000)
)auto_increment=10000;

INSERT INTO weibocatch.w_user
(wid,wname,recon,color,flag)
VALUES
('2468833122','poiu1235',0,'',0);

INSERT INTO weibocatch.w_user
(wid,wname,recon,color,flag)
VALUES
('2430104687','江森开根号',0,0,0);

SHOW GLOBAL VARIABLES LIKE 'auto_incre%'; -- 全局变量
select * from weibocatch.w_relation;
select NOW();
delete from weibocatch.pic_reg;
delete from weibocatch.w_transfer;

alter table weibocatch.pic_reg AUTO_INCREMENT=10000;
select * from weibocatch.w_user;
update weibocatch.w_user set flag=0 where wid ='2430104687'

insert 和 update 操作完成后一定要 self.conn.commit(),不然不会改变数据库的

                try:
                     xxxx;
                except Exception as err:
                    self.conn.rollback()
                    print err
                finally:
                    #关闭连接,释放资源     
                    cursor.close();
                    self.conn.close() 

if(wconn.tag.strip()):#判断字符串不为空


把准备数据库连接的过程写在一个函数里,这样每次临时要用在临时去调用
self.inition()#因为mysql 连接很容易超时就断开了,默认10s,所以索性每次都用的时候在申明


#插入一条记录 全部用%s作占位符号,即使是数字也不要用%d 
#因为这个插件程序会帮你转换的,字符串会自动给你加‘‘,如果数字不会加


#使用这函数向一个给定Connection对象返回的值是该Connection对象产生
#对影响AUTO_INCREMENT列的最新语句第一个AUTO_INCREMENT值的。
#这个值不能被其它Connection对象的影响,即它们产生它们自己的AUTO_INCREMENT值。
#第二、LAST_INSERT_ID 是与table无关的,
#如果向表a插入数据后,再向表b插入数据,LAST_INSERT_ID返回表b中的Id值。
cursor.execute("SELECT LAST_INSERT_ID()")
#"只获取一条记录:"   
result = cursor.fetchone();#这个返回的是一个tuple,一个元素的元组后面都会跟一个”,“。没实际意义

#表示从第二个取到倒数第二个,去掉模式串中两头的单引号
loginweb=loginweb[1:-1]

#reload(sys) 
#sys.setdefaultencoding( "utf-8" )#这个是因为插件报错,但是程序正常。

#str1.decode('gb2312'),表示将gb2312编码的字符串str1转换成unicode编码
#str2.encode('gb2312'),表示将unicode编码的字符串str2转换成gb2312编码。 

#设置读取目录是当前目录
#但是虽然存文件方便了,但是由于当前路径变化导致不能正确读取当前cookies文件路径
#os.chdir("/home/luis/workspace/weibo-catch/picture/")
#os.getcwd()<pre name="code" class="python">#所以直接写完整路径会省去很多麻烦
ppth="/home/luis/workspace/weibo-catch/picture/"

data = urllib.urlopen(picurl).read()
f = open(name,'wb',8192)#设置文件缓冲区为8M大小,有的图片较大怕一次存不完
f.write(data)
f.close#close 方法相当于把缓冲区flush后再关闭的。

#因为f.read()方法读到的是str类型,默认是utf8, 可以直接用str类型进行查找,不用进行转码decode操作

if(countt==10):
   subprocess.call("pause",shell=True)#让程序暂停替换了os.system('pause')方法 或者用if(raw_input()): pass

#注意每个beautifulsoup对象截取出来的对象都是”特定的标签“对象,可以直接进行beautifulsoup相关操作
#而不是字符串类型,如果要进行一些字符串操作,必须要转成str() 类型。
#Beautiful Soup用了 编码自动检测 子库来识别当前文档编码并转换成Unicode编码
#通过Beautiful Soup输出文档时,不管输入文档是什么编码方式,输出编码均为UTF-8编码
#但是像index 和 单纯的 find 方法会转成unicode编码方式再来操作,执行完后就要转码成utf8操作。
#即先转码再转回来(先decode(‘utf8’)再encode(‘utf8’))

time.sleep(2)#每次完成一个任务就暂停2秒

#一般的字符串操作有rfind 从后前前找,beautifulsoup没有

#不可以用remove 方法,包括del pop这些方法只有list有
#remove 是按照关键字删除,但是del 和pop 是按照索引删除

#表示去掉字串两头的空格,s.strip(rm)删除s字符串中开头、结尾处,位于rm序列里的字符,      
#当rm为空时,默认删除空白符(包括'\n', '\r',  '\t',  ' ')

#get_text() 方法可以拿到全部内容,而content数组拿到的是被内层标签分段内容块。
transfercon=contentcut2.get_text()


s="原图"
sx=s.decode('utf-8')#千万要注意不能把两句写在一起,动态编译不会识别的
picurl=contentcut2.parent.next_sibling.find('a',text=sx)["href"]

#补充一点,双引号会对内容进行转义,单引号则不会


#为什么可以不用初始化对象就可以直接通过类名调用方法,当然是不可以的,运行的时候就会报错

#(这个错误很隐蔽,在编码过程中并不会报错,因为python是解释执行的
#在运行过程中就会找不到这个方法。
#解决方式,要么实例化对象后再调用方法,要么用“类方法”即在类名上加上@classmethod 修饰
#并且要把方法的第一个默认参数写成cls)这里我采用第二种方法,在类名上加修饰符

#类方法,用classmethod来进行修饰

#类方法的隐含调用参数是类,而类实例方法的隐含调用参数是类的实例,静态方法没有隐含调用参数
@classmethod
def findweibo(cls,sweb):

#注意:join()方法的位置是在for循环外的,也就是说必须等待for循环里的两个进程都结束后,才去执行主进程。
#或者结合flag 和while 来判断 程程是否还有效 
#is_alive(): Return whether the thread is alive.

##findAll 拿到的是一系列标签的list

resp1 = soup.find('input', attrs = {'name':'vk'})['value']


#中文正则表达式
#前面带一个u 不是 r 并且 中文字符的编码范围是[\u4E00-\u9FA5]
reg=u"<a href=('http://login.weibo.cn/[^\u4E00-\u9FA5]*?)[>]+?[\u4E00-\u9FA5]{2}</a>"


反正所有项目文件都在github 上 https://github.com/poiu1235/weibo-catch:




版权声明:本文为博主原创文章,未经博主允许不得转载。

python 爬虫3 新浪微博 爬虫 实战

标签:python   github   新浪微博   爬虫   源代码   

原文地址:http://blog.csdn.net/yuyi_000/article/details/48103295

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!