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 相关文章推荐
使用70行Python代码实现一个递归下降解析器的教程
Apr 17 Python
Python自动化部署工具Fabric的简单上手指南
Apr 19 Python
Python中struct模块对字节流/二进制流的操作教程
Jan 21 Python
浅谈使用Python变量时要避免的3个错误
Oct 30 Python
彻底搞懂Python字符编码
Jan 23 Python
使用Django2快速开发Web项目的详细步骤
Jan 06 Python
Scrapy框架爬取Boss直聘网Python职位信息的源码
Feb 22 Python
利用python实现短信和电话提醒功能的例子
Aug 08 Python
Python数据可视化:幂律分布实例详解
Dec 07 Python
Django中文件上传和文件访问微项目的方法
Apr 27 Python
Python超简单容易上手的画图工具库推荐
May 10 Python
python中%格式表达式实例用法
Jun 18 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动态变静态原理
2006/11/25 PHP
11个PHP 分页脚本推荐
2011/08/15 PHP
redis 队列操作的例子(php)
2012/04/12 PHP
joomla jce editor 解决上传中文名文件失败问题
2013/06/09 PHP
PHP小教程之实现链表
2014/06/09 PHP
jQuery向下滚动即时加载内容实现的瀑布流效果
2016/01/07 PHP
取得窗口大小 兼容所有浏览器的js代码
2011/08/09 Javascript
JS实现鼠标箭头变成一个燃烧烛光效果的方法
2015/02/28 Javascript
jquery中添加属性和删除属性
2015/06/03 Javascript
js实现页面跳转的几种方法小结
2016/05/16 Javascript
利用VUE框架,实现列表分页功能示例代码
2017/01/12 Javascript
JavaScript实现随机数生成器(去重)
2017/10/13 Javascript
JavaScript实现JSON合并操作示例【递归深度合并】
2018/09/07 Javascript
vue3.0 CLI - 2.6 - 组件的复用入门教程
2018/09/14 Javascript
关于微信小程序map组件z-index的层级问题分析
2019/07/09 Javascript
原生js实现俄罗斯方块
2020/10/20 Javascript
在Python的Django框架中simple-todo工具的简单使用
2015/05/30 Python
Python中线程编程之threading模块的使用详解
2015/06/23 Python
Python多进程机制实例详解
2015/07/02 Python
python实现ping的方法
2015/07/06 Python
Python reduce()函数的用法小结
2017/11/15 Python
Python cookbook(数据结构与算法)实现优先级队列的方法示例
2018/02/18 Python
PyQt5固定窗口大小的方法
2019/06/18 Python
django admin管理工具自定义时间区间筛选器DateRangeFilter介绍
2020/05/19 Python
浅析Python 中的 WSGI 接口和 WSGI 服务的运行
2020/12/09 Python
selenium如何定位span元素的实现
2021/01/13 Python
美国在线购买内衣网站:HerRoom
2020/02/22 全球购物
镇创先争优活动总结
2014/08/28 职场文书
法制教育演讲稿
2014/09/10 职场文书
镇政府副镇长群众路线专题民主生活会对照检查材料
2014/09/19 职场文书
买卖合同协议书范本
2014/10/18 职场文书
2014年环卫工作总结
2014/11/22 职场文书
2014收银员工作总结范文
2014/12/16 职场文书
2015年文秘个人工作总结
2015/10/14 职场文书
Pytorch可视化的几种实现方法
2021/06/10 Python
nginx location 带斜杠【 / 】与不带的区别
2022/04/13 Servers