python编写网页爬虫脚本并实现APScheduler调度


Posted in Python onJuly 28, 2014

前段时间自学了python,作为新手就想着自己写个东西能练习一下,了解到python编写爬虫脚本非常方便,且最近又学习了MongoDB相关的知识,万事具备只欠东风。

程序的需求是这样的,爬虫爬的页面是京东的电子书网站页面,每天会更新一些免费的电子书,爬虫会把每天更新的免费的书名以第一时间通过邮件发给我,通知我去下载。

一、编写思路:

1.爬虫脚本获取当日免费书籍信息

2.把获取到的书籍信息与数据库中的已有信息作比较,如果书籍存在不做任何操作,书籍不存在,执行插入数据库的操作,把数据的信息存入MongoDB

3.执行数据库插入操作时,把更新的数据以邮件的形式发送出来

4.用APScheduler调度框架完成python脚本调度

二、脚本的主要知识点:

1.python简单爬虫

本次用到的模块有urllib2用来抓取页面,导入模块如下:

import urllib2
from sgmllib import SGMLParser

urlopen()方法获取网页HTML源码,都存储在content中,listhref()类主要的功能是解析HTML代码,处理HTML类型的半结构化文档。

content = urllib2.urlopen('http://sale.jd.com/act/yufbrhZtjx6JTV.html').read()
listhref = ListHref()
listhref.feed(content)

listhref()类代码可以在下面全部代码中查询到,这里只说几个关键点:

listhref()类继承了SGMLParser 类并重写了其中的内部方法。SGMLParser 将HTML分解成有用的片段,比如开始标记和结束标记。一旦成功地分解出某个数据为一个有用的片段,它会根据所发现的数据,调用一个自身内部的方法。为了使用这个分析器,您需要子类化 SGMLParser类,并且重写父类的这些方法。

SGMLParser 将 HTML 分析成不同类数据及标记,然后对每一类调用单独的方法:
开始标记 (Start_tag)
是一个开始一个块的 HTML 标记,像 <html>,<head>,<body> , <pre> 等,或是一个独一的标记,象 <br> 或 <img> 等。本例当它找到一个开始标记<a>,SGMLParser将查找名为 start_a或do_a的方法。如果找到了,SGMLParser会使用这个标记的属性列表来调用这个方法;否则,它用这个标记的名字和属性列表来调用unknown_starttag方法。
结束标记 (End_tag)
是结束一个块的HTML标记,像 </html>,</head>,</body> 或 </pre> 等。本例中当找到一个结束标记时,SGMLParser 将查找名为end_a的方法。如果找到,SGMLParser调用这个方法,否则它使用标记的名字来调用unknown_endtag。
文本数据(Text data)
获取文本块,当不满足其它各类别的任何标记时,调用handle_data获取文本。

以下的几类在本文中没有用到
字符引用 (Character reference)
用字符的十进制或等同的十六进制来表示的转义字符,当找到该字符,SGMLParser用字符调用 handle_charref 。
实体引用 (Entity reference)
HTML实体,像&ref,当找到该实体,SGMLParser实体的名字调用handle_entityref。
注释 (Comment)
HTML注释, 包括在 <!-- ... -->之间。当找到,SGMLParser用注释内容调用handle_comment。
处理指令 (Processing instruction)
HTML处理指令,包括在 <? ... > 之间。当找到,SGMLParser用指令内容调 handle_pi。
声明 (Declaration)
HTML声明,如DOCTYPE,包括在 <! ... >之间。当找到,SGMLParser用声明内容调用handle_decl。

具体的说明参考API:http://docs.python.org/2/library/sgmllib.html?highlight=sgmlparser#sgmllib.SGMLParser

2.python操作MongoDB数据库

首先要安装python对mongoDB的驱动PyMongo,下载地址:https://pypi.python.org/pypi/pymongo/2.5

导入模块

import pymongo

连接数据库服务器127.0.0.1和切换到所用数据库mydatabase

mongoCon=pymongo.Connection(host="127.0.0.1",port=27017)
db= mongoCon.mydatabase

查找数据库相关书籍信息,book为查找的collection

bookInfo = db.book.find_one({"href":bookItem.href})

为数据库插入书籍信息,python支持中文,但是对于中文的编码和解码还是比较复杂,相关解码和编码请参考http://blog.csdn.net/mayflowers/article/details/1568852

b={
"bookname":bookItem.bookname.decode('gbk').encode('utf8'),
"href":bookItem.href,
"date":bookItem.date
}
db.book.insert(b,safe=True)

关于PyMongo请参考API文档http://api.mongodb.org/python/2.0.1/

3.python发送邮件

导入邮件模块

# Import smtplib for the actual sending function
import smtplib
from email.mime.text import MIMEText

"localhost"为邮件服务器地址

msg = MIMEText(context) #文本邮件的内容
msg['Subject'] = sub #主题
msg['From'] = "my@vmail.cn" #发信人
msg['To'] = COMMASPACE.join(mailto_list) #收信人列表

def send_mail(mailto_list, sub, context): 
COMMASPACE = ','
mail_host = "localhost"
me = "my@vmail.cn"
# Create a text/plain message
msg = MIMEText(context) 
msg['Subject'] = sub 
msg['From'] = "my@vmail.cn"
msg['To'] = COMMASPACE.join(mailto_list)

send_smtp = smtplib.SMTP(mail_host) 

send_smtp.sendmail(me, mailto_list, msg.as_string()) 
send_smtp.close()

应用文档:http://docs.python.org/2/library/email.html?highlight=smtplib#

4.Python调度框架ApScheduler

下载地址https://pypi.python.org/pypi/APScheduler/2.1.0

官方文档:http://pythonhosted.org/APScheduler/#faq

API:http://pythonhosted.org/APScheduler/genindex.html

安装方法:下载之后解压缩,然后执行python setup.py install,导入模块

from apscheduler.scheduler import Scheduler

ApScheduler配置比较简单,本例中只用到了add_interval_job方法,在每间隔一段时间后执行任务脚本,本例中的间隔是30分钟。可参考实例文章http://flykite.blog.51cto.com/4721239/832036

# Start the scheduler 
sched = Scheduler()
sched.daemonic = False 
sched.add_interval_job(job,minutes=30) 
sched.start()

关于daemonic参数:

apscheduler会创建一个线程,这个线程默认是daemon=True,也就是默认的是线程守护的。

在上面的代码里面,要是不加上sched.daemonic=False的话,这个脚本就不会按时间运行。

因为脚本要是没有sched.daemonic=False,它会创建一个守护线程。这个过程中,会创建scheduler的实例。但是由于脚本运行速度很快,主线程mainthread会马上结束,而此时定时任务的线程还没来得及执行,就跟随主线程结束而结束了。(守护线程和主线程之间的关系决定的)。要让脚本运行正常,必须设置该脚本为非守护线程。sched.daemonic=False

附:全部脚本代码

All Code

#-*- coding: UTF-8 -*-
import urllib2
from sgmllib import SGMLParser
import pymongo
import time
# Import smtplib for the actual sending function
import smtplib
from email.mime.text import MIMEText
from apscheduler.scheduler import Scheduler

#get freebook hrefs
class ListHref(SGMLParser):
def __init__(self):
SGMLParser.__init__(self)
self.is_a = ""
self.name = []
self.freehref=""
self.hrefs=[]

def start_a(self, attrs):
self.is_a = 1
href = [v for k, v in attrs if k == "href"]
self.freehref=href[0]

def end_a(self):
self.is_a = ""

def handle_data(self, text):
if self.is_a == 1 and text.decode('utf8').encode('gbk')=="限时免费":
self.hrefs.append(self.freehref)
#get freebook Info
class FreeBook(SGMLParser):
def __init__(self):
SGMLParser.__init__(self)
self.is_title=""
self.name = ""
def start_title(self, attrs):
self.is_title = 1
def end_title(self):
self.is_title = ""
def handle_data(self, text):
if self.is_title == 1: 
self.name=text
#Mongo Store Module
class freeBookMod:
def __init__(self, date, bookname ,href):
self.date=date
self.bookname=bookname
self.href=href

def get_book(bookList):
content = urllib2.urlopen('http://sale.jd.com/act/yufbrhZtjx6JTV.html').read()
listhref = ListHref()
listhref.feed(content)

for href in listhref.hrefs:
content = urllib2.urlopen(str(href)).read()
listbook=FreeBook()
listbook.feed(content)
name = listbook.name
n= name.index('》')
#print (name[0:n+2])
freebook=freeBookMod(time.strftime('%Y-%m-%d',time.localtime(time.time())),name[0:n+2],href)
bookList.append(freebook)
return bookList

def record_book(bookList,context,isSendMail):
# DataBase Operation
mongoCon=pymongo.Connection(host="127.0.0.1",port=27017)
db= mongoCon.mydatabase
for bookItem in bookList:
bookInfo = db.book.find_one({"href":bookItem.href})

if not bookInfo:
b={
"bookname":bookItem.bookname.decode('gbk').encode('utf8'),
"href":bookItem.href,
"date":bookItem.date
}
db.book.insert(b,safe=True)
isSendMail=True
context=context+bookItem.bookname.decode('gbk').encode('utf8')+','
return context,isSendMail 

#Send Message
def send_mail(mailto_list, sub, context): 
COMMASPACE = ','
mail_host = "localhost"
me = "my@vmail.cn"
# Create a text/plain message
msg = MIMEText(context) 
msg['Subject'] = sub 
msg['From'] = "my@vmail.cn"
msg['To'] = COMMASPACE.join(mailto_list)

send_smtp = smtplib.SMTP(mail_host) 

send_smtp.sendmail(me, mailto_list, msg.as_string()) 
send_smtp.close() 

#Main job for scheduler 
def job(): 
bookList=[]
isSendMail=False; 
context="Today free books are"
mailto_list=["mailto@mail.cn"]
bookList=get_book(bookList)
context,isSendMail=record_book(bookList,context,isSendMail)
if isSendMail==True: 
send_mail(mailto_list,"Free Book is Update",context)

if __name__=="__main__": 
# Start the scheduler 
sched = Scheduler()
sched.daemonic = False 
sched.add_interval_job(job,minutes=30) 
sched.start()
Python 相关文章推荐
Python中列表、字典、元组、集合数据结构整理
Nov 20 Python
Python实现简单截取中文字符串的方法
Jun 15 Python
Python基于pygame实现的弹力球效果(附源码)
Nov 11 Python
Python实现PS图像明亮度调整效果示例
Jan 23 Python
Python开发虚拟环境使用virtualenvwrapper的搭建步骤教程图解
Sep 19 Python
Scrapy框架使用的基本知识
Oct 21 Python
python中for循环把字符串或者字典添加到列表的方法
Jul 20 Python
python列表每个元素同增同减和列表元素去空格的实例
Jul 20 Python
python打印n位数“水仙花数”(实例代码)
Dec 25 Python
python相对企业语言优势在哪
Jun 12 Python
解决Python 写文件报错TypeError的问题
Oct 23 Python
Python开发之QT解决无边框界面拖动卡屏问题(附带源码)
May 27 Python
python调用新浪微博API项目实践
Jul 28 #Python
python中的sort方法使用详解
Jul 25 #Python
python实现监控linux性能及进程消耗性能的方法
Jul 25 #Python
python的dict,set,list,tuple应用详解
Jul 24 #Python
Python常见数据结构详解
Jul 24 #Python
python海龟绘图实例教程
Jul 24 #Python
python实现绘制树枝简单示例
Jul 24 #Python
You might like
PHP简洁函数(PHP简单明了函数语法)
2012/06/10 PHP
php防止伪造的数据从URL提交方法
2014/06/27 PHP
PHP日期函数date格式化UNIX时间的方法
2015/03/19 PHP
PHP程序员不应该忽略的3点
2015/10/09 PHP
Zend Framework教程之Bootstrap类用法概述
2016/03/14 PHP
JS 动态获取节点代码innerHTML分析 [IE,FF]
2009/11/30 Javascript
javascript + jquery实现定时修改文章标题
2014/03/19 Javascript
javascript操作excel生成报表示例
2014/05/08 Javascript
javascript最基本的函数汇总
2015/06/25 Javascript
JS使用正则表达式除去字符串中重复字符的方法
2015/11/05 Javascript
JavaScript面试题大全(推荐)
2016/09/22 Javascript
解决Angular.Js与Django标签冲突的方案
2016/12/20 Javascript
Bootstrap源码解读标签、徽章、缩略图和警示框(8)
2016/12/26 Javascript
JS 中使用Promise 实现红绿灯实例代码(demo)
2017/10/20 Javascript
jQuery实现的简单拖拽功能示例【测试可用】
2018/08/14 jQuery
vue cli4下环境变量和模式示例详解
2020/04/09 Javascript
jquery实现轮播图特效
2020/04/12 jQuery
Vue实现背景更换颜色操作
2020/07/17 Javascript
[02:43]2014DOTA2国际邀请赛 官方Alliance战队纪录片
2014/07/14 DOTA
在Python中使用swapCase()方法转换大小写的教程
2015/05/20 Python
简单介绍使用Python解析并修改XML文档的方法
2015/10/15 Python
利用Python开发微信支付的注意事项
2016/08/19 Python
Python列表list解析操作示例【整数操作、字符操作、矩阵操作】
2017/07/25 Python
Jupyter notebook远程访问服务器的方法
2018/05/24 Python
Django 使用Ajax进行前后台交互的示例讲解
2018/05/28 Python
关于torch.optim的灵活使用详解(包括重写SGD,加上L1正则)
2020/02/20 Python
Python如何测试stdout输出
2020/08/10 Python
HTML+CSS+JavaScript实现图片3D展览的示例代码
2020/10/12 HTML / CSS
Skip Hop官网:好莱坞宝宝挚爱品牌
2018/06/17 全球购物
趣味体育活动方案
2014/02/08 职场文书
2014年幼儿园学期工作总结
2014/12/05 职场文书
小班教师个人总结
2015/02/05 职场文书
高中运动会前导词
2015/07/20 职场文书
WordPress多语言翻译插件 - WPML使用教程
2021/04/01 PHP
java如何实现获取客户端ip地址的示例代码
2022/04/07 Java/Android
Elasticsearch 配置详解
2022/04/19 Java/Android