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处理图片之PIL模块简单使用方法
May 11 Python
Python字符串特性及常用字符串方法的简单笔记
Jan 04 Python
详解Python的Django框架中manage命令的使用与扩展
Apr 11 Python
python3.4用函数操作mysql5.7数据库
Jun 23 Python
python 对给定可迭代集合统计出现频率,并排序的方法
Oct 18 Python
python调用webservice接口的实现
Jul 12 Python
python爬取Ajax动态加载网页过程解析
Sep 05 Python
Django的CVB实例详解
Feb 10 Python
解决pytorch多GPU训练保存的模型,在单GPU环境下加载出错问题
Jun 23 Python
python解释器安装教程的方法步骤
Jul 02 Python
浅谈keras中Dropout在预测过程中是否仍要起作用
Jul 09 Python
pycharm无法安装cv2模块问题
May 20 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访问MYSQL数据库封装类(附函数说明)
2010/12/04 PHP
php实现分页工具类分享
2014/01/09 PHP
PHP 生成微信红包代码简单
2016/03/25 PHP
详解PHP防止盗链防止迅雷下载的方法
2017/04/26 PHP
Javascript SHA-1:Secure Hash Algorithm
2006/12/20 Javascript
javascript获得服务器端控件的ID的实现代码
2011/12/28 Javascript
JavaScript获取网页支持表单字符集的方法
2015/04/02 Javascript
javascript实现根据时间段显示问候语的方法
2015/06/18 Javascript
javascript基础知识分享之类与函数化
2016/02/13 Javascript
深入理解Angular2 模板语法
2016/08/07 Javascript
JS中静态页面实现微信分享功能
2017/02/06 Javascript
修改vue+webpack run build的路径方法
2018/09/01 Javascript
jQuery ajax仿Google自动提示SearchSuggess功能示例
2019/03/28 jQuery
查找Vue中下标的操作(some和findindex)
2020/08/12 Javascript
[00:55]2015国际邀请赛中国区预选赛5月23日——28日约战上海
2015/05/25 DOTA
解读Python中degrees()方法的使用
2015/05/18 Python
Python爬取qq music中的音乐url及批量下载
2017/03/23 Python
OpenCV-Python实现轮廓检测实例分析
2018/01/05 Python
Centos7 下安装最新的python3.8
2019/10/28 Python
python中Lambda表达式详解
2019/11/20 Python
Python3 Tensorlfow:增加或者减小矩阵维度的实现
2020/05/22 Python
html5新增的属性和废除的属性简要概述
2013/02/20 HTML / CSS
HTML5中判断用户是否正在浏览页面的方法
2014/05/03 HTML / CSS
清明节演讲稿
2014/05/27 职场文书
社区精神文明建设汇报材料
2014/08/17 职场文书
股指期货心得体会
2014/09/13 职场文书
交通事故和解协议书
2014/09/25 职场文书
2015年社区综治宣传月活动总结
2015/03/25 职场文书
小学语文新课改心得体会
2016/01/22 职场文书
党风廉政建设心得体会
2019/05/21 职场文书
MySQL 慢查询日志深入理解
2021/04/22 MySQL
Python入门学习之类的相关知识总结
2021/05/25 Python
Python scrapy爬取起点中文网小说榜单
2021/06/13 Python
Java8中Stream的一些神操作
2021/11/02 Java/Android
js前端图片加载异常兜底方案
2022/06/21 Javascript
使用opencv-python如何打开USB或者笔记本前置摄像头
2022/06/21 Python