- Urllib的用法及异常处理
- Beautiful Soup的简单应用
- MySQLdb的基础用法
框架思路
首先我们随便找一个分类地址,外语学习 – 爱问知识人,打开之后可以看到一系列的问题列表。
我们在这个页面需要获取的东西有:
总的页码数,每一页的所有问题链接。
接下来我们需要遍历所有的问题,来抓取每一个详情页面,提取问题,问题内容,回答者,回答时间,回答内容。
最后,我们需要把这些内容存储到数据库中。
要点简析
1.日志输出
日志输出,我们要输出时间和爬取的状态,比如像下面这样:
[2015-08-10 03:05:20] 113011 号问题存在其他答案 我个人认为应该是樱桃沟很美的
[2015-08-10 03:05:20] 保存到数据库,此问题的ID为 113011
[2015-08-10 03:05:20] 当前爬取第 2 的内容,发现一个问题 百度有一个地方,花儿带着芳香,水儿流淌奔腾是什么意思 多多帮忙哦 回答数量 1
[2015-08-10 03:05:19] 保存到数据库,此问题的ID为 113010
所以,我们需要引入时间函数,然后写一个获取当前时间的函数
import time
#获取当前时间
def getCurrentTime(self):
return time.strftime('[%Y-%m-%d %H:%M:%S]',time.localtime(time.time()))
#获取当前时间
def getCurrentDate(self):
return time.strftime('%Y-%m-%d',time.localtime(time.time()))
然后我们需要将缓冲区设置输出到log中
f_handler=open('out.log', 'w')
sys.stdout=f_handler
这样,所有的print语句输出的内容就会保存到out.log文件中了。
4.保存到数据库
在这里,想实现一个通用的方法,就是把存储的一个个内容变成字典的形式,然后执行插入语句的时候,自动构建对应的sql语句,插入数据。
#构造最佳答案的字典
good_ans_dict = {
"text": good_ans[0],
"answerer": good_ans[1],
"date": good_ans[2],
"is_good": str(good_ans[3]),
"question_id": str(insert_id)
}
构造sql语句并插入到数据库的方法如下:
#插入数据
def insertData(self, table, my_dict):
try:
self.db.set_character_set('utf8')
cols = ', '.join(my_dict.keys())
values = '"," '.join(my_dict.values())
sql = "INSERT INTO %s (%s) VALUES (%s)" % (table, cols, '"'+values+'"')
try:
result = self.cur.execute(sql)
insert_id = self.db.insert_id()
self.db.commit()
#判断是否执行成功
if result:
return insert_id
else:
return 0
except MySQLdb.Error,e:
#发生错误时回滚
self.db.rollback()
#主键唯一,无法插入
if "key 'PRIMARY'" in e.args[1]:
print self.getCurrentTime(),"数据已存在,未插入数据"
else:
print self.getCurrentTime(),"插入数据失败,原因 %d: %s" % (e.args[0], e.args[1])
except MySQLdb.Error,e:
print self.getCurrentTime(),"数据库错误,原因%d: %s" % (e.args[0], e.args[1])
这里我们只需要传入那个字典,便会构建出对应字典键值和键名的sql语句,完成插入。
5.PHP读取日志
将运行结果输出到了日志里,那么怎么查看日志呢?很简单,在这里提供两种方法
方法一:
PHP倒序输出所有日志内容
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="refresh" content = "5">
</head>
<body>
<?php
$fp = file("out.log");
if ($fp) {
for($i = count($fp) - 1;$i >= 0; $i --)
echo $fp[$i]."<br>";
}
?>
</body>
</html>
此方法可以看到所有的输入日志,但是如果日志太大了,那么就会报耗费内存太大,无法输出。为此我们就有了第二种方法,利用linux命令,输出后十行内容。
方法二:
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="refresh" content = "5">
</head>
<body>
<?php
$ph = popen('tail -n 100 out.log','r');
while($r = fgets($ph)){
echo $r."<br>";
}
pclose($ph);
?>
</body>
</html>
上面两种方法都是5秒刷新一次网页来查看最新的日志。
源代码放送
spider.py
# -*- coding:utf-8 -*-
import urllib
import urllib2
import re
import time
import types
import page
import mysql
import sys
from bs4 import BeautifulSoup
class Spider:
#初始化
def __init__(self):
self.page_num = 1
self.total_num = None
self.page_spider = page.Page()
self.mysql = mysql.Mysql()
#获取当前时间
def getCurrentTime(self):
return time.strftime('[%Y-%m-%d %H:%M:%S]',time.localtime(time.time()))
#获取当前时间
def getCurrentDate(self):
return time.strftime('%Y-%m-%d',time.localtime(time.time()))
#通过网页的页码数来构建网页的URL
page.py
# -*- coding:utf-8 -*-
import urllib
import urllib2
import re
import time
import types
import tool
from bs4 import BeautifulSoup
#抓取分析某一问题和答案
class Page:
def __init__(self):
self.tool = tool.Tool()
#获取当前时间
def getCurrentDate(self):
return time.strftime('%Y-%m-%d',time.localtime(time.time()))
#获取当前时间
def getCurrentTime(self):
return time.strftime('[%Y-%m-%d %H:%M:%S]',time.localtime(time.time()))
#通过页面的URL来获取页面的代码
def getPageByURL(self, url):
try:
request = urllib2.Request(url)
response = urllib2.urlopen(request)
return response.read().decode("utf-8")
except urllib2.URLError, e:
tool.py
#-*- coding:utf-8 -*-
import re
#处理页面标签类
class Tool:
#将超链接广告剔除
removeADLink = re.compile('<div class="link_layer.*?</div>')
#去除img标签,1-7位空格,
removeImg = re.compile('<img.*?>| {1,7}| ')
#删除超链接标签
removeAddr = re.compile('<a.*?>|</a>')
#把换行的标签换为\n
replaceLine = re.compile('<tr>|<div>|</div>|</p>')
#将表格制表<td>替换为\t
replaceTD= re.compile('<td>')
#将换行符或双换行符替换为\n
replaceBR = re.compile('<br><br>|<br>')
#将其余标签剔除
removeExtraTag = re.compile('<.*?>')
mysql.py
# -*- coding:utf-8 -*-
import MySQLdb
import time
class Mysql:
#获取当前时间
def getCurrentTime(self):
return time.strftime('[%Y-%m-%d %H:%M:%S]',time.localtime(time.time()))
#数据库初始化
def __init__(self):
try:
self.db = MySQLdb.connect('ip','username','password','db_name')
self.cur = self.db.cursor()
except MySQLdb.Error,e:
print self.getCurrentTime(),"连接数据库错误,原因%d: %s" % (e.args[0], e.args[1])
#插入数据
def insertData(self, table, my_dict):
try:
self.db.set_character_set('utf8')
cols = ', '.join(my_dict.keys())
values = '"," '.join(my_dict.values())
sql = "INSERT INTO %s (%s) VALUES (%s)" % (table, cols, '"'+values+'"')
try:
result = self.cur.execute(sql)
insert_id = self.db.insert_id()
self.db.commit()
#判断是否执行成功
if result:
return insert_id
else:
return 0
except MySQLdb.Error,e:
#发生错误时回滚
self.db.rollback()
#主键唯一,无法插入
if "key 'PRIMARY'" in e.args[1]:
print self.getCurrentTime(),"数据已存在,未插入数据"
else:
print self.getCurrentTime(),"插入数据失败,原因 %d: %s" % (e.args[0], e.args[1])
except MySQLdb.Error,e:
print self.getCurrentTime(),"数据库错误,原因%d: %s" % (e.args[0], e.args[1])
数据库建表SQL如下:
CREATE TABLE IF NOT EXISTS `iask_answers` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增ID',
`text` text NOT NULL COMMENT '回答内容',
`question_id` int(18) NOT NULL COMMENT '问题ID',
`answerer` varchar(255) NOT NULL COMMENT '回答者',
`date` varchar(255) NOT NULL COMMENT '回答时间',
`is_good` int(11) NOT NULL COMMENT '是否是最佳答案',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `iask_questions` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '问题ID',
`text` text NOT NULL COMMENT '问题内容',
`questioner` varchar(255) NOT NULL COMMENT '提问者',
`date` date NOT NULL COMMENT '提问时间',
`ans_num` int(11) NOT NULL COMMENT '回答数量',
`url` varchar(255) NOT NULL COMMENT '问题链接',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
运行的时候执行如下命令即可:nohup python spider.py &
运行结果查看
我们把PHP文件和log文件放在同一目录下,运行PHP文件,便可以看到如下的内容: