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 字符串split的用法分享
Mar 23 Python
Python实现抓取百度搜索结果页的网站标题信息
Jan 22 Python
python顺序的读取文件夹下名称有序的文件方法
Jul 11 Python
python基础学习之如何对元组各个元素进行命名详解
Jul 12 Python
Python检测数据类型的方法总结
May 20 Python
用Python识别人脸,人种等各种信息
Jul 15 Python
python提取xml里面的链接源码详解
Oct 15 Python
flask框架json数据的拿取和返回操作示例
Nov 28 Python
Python如何将函数值赋给变量
Apr 28 Python
python中pathlib模块的基本用法与总结
Aug 17 Python
python 用pandas实现数据透视表功能
Dec 21 Python
Python数据清洗工具之Numpy的基本操作
Apr 22 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
收音机怀古---春雷3P7图片欣赏
2021/03/02 无线电
PHP实现的多维数组排序算法分析
2018/02/10 PHP
JS setCapture 区域外事件捕捉
2010/03/18 Javascript
Backbone.js的一些使用技巧
2015/07/01 Javascript
jQuery插件Flexslider实现图片轮播、图文结合滑动切换效果
2020/04/16 Javascript
js实现适合新闻类图片的轮播效果
2017/02/05 Javascript
jQuery使用eraser.js插件实现擦除、刮刮卡效果的方法【附eraser.js下载】
2017/04/28 jQuery
JS和jQuery通过this获取html标签中的属性值(实例代码)
2017/09/11 jQuery
JavaScript fetch接口案例解析
2018/08/30 Javascript
详解为生产环境编译Angular2应用的方法
2018/12/10 Javascript
JavaScript使用面向对象实现的拖拽功能详解
2019/06/12 Javascript
jQuery属性选择器用法实例分析
2019/06/28 jQuery
vue从一个页面跳转到另一个页面并携带参数的解决方法
2019/08/12 Javascript
vue resource发送请求的几种方式
2019/09/30 Javascript
Node对CommonJS的模块规范
2019/11/06 Javascript
js判断密码强度的方法
2020/03/18 Javascript
vue样式穿透 ::v-deep的具体使用
2020/06/04 Javascript
JS实现炫酷雪花飘落效果
2020/08/19 Javascript
Python 提取dict转换为xml/json/table并输出的实现代码
2016/08/28 Python
git使用.gitignore设置不生效或不起作用问题的解决方法
2017/06/01 Python
Python下使用Scrapy爬取网页内容的实例
2018/05/21 Python
python逆序打印各位数字的方法
2018/06/25 Python
python画图系列之个性化显示x轴区段文字的实例
2018/12/13 Python
Pytorch 数据加载与数据预处理方式
2019/12/31 Python
Python 格式化输出_String Formatting_控制小数点位数的实例详解
2020/02/04 Python
html5开发之viewport使用
2013/10/17 HTML / CSS
英国家庭和商业健身器材购物网站:Fitness Options
2018/07/05 全球购物
时尚孕妇装:Ingrid & Isabel
2019/05/08 全球购物
升职自荐信范文
2013/10/05 职场文书
竞聘演讲稿范文
2014/01/12 职场文书
民主生活会批评与自我批评总结
2014/10/17 职场文书
工作态度不好检讨书
2015/05/06 职场文书
学生党支部工作总结2015
2015/05/26 职场文书
2015年小学生国庆节演讲稿
2015/07/30 职场文书
2019终止劳动合同协议书最新范本!
2019/07/09 职场文书
新手入门Mysql--概念
2021/06/18 MySQL