python編寫(xiě)網(wǎng)頁(yè)爬蟲(chóng)腳本并實(shí)現(xiàn)APScheduler調(diào)度
前段時(shí)間自學(xué)了python,作為新手就想著自己寫(xiě)個(gè)東西能練習(xí)一下,了解到python編寫(xiě)爬蟲(chóng)腳本非常方便,且最近又學(xué)習(xí)了MongoDB相關(guān)的知識(shí),萬(wàn)事具備只欠東風(fēng)。
程序的需求是這樣的,爬蟲(chóng)爬的頁(yè)面是京東的電子書(shū)網(wǎng)站頁(yè)面,每天會(huì)更新一些免費(fèi)的電子書(shū),爬蟲(chóng)會(huì)把每天更新的免費(fèi)的書(shū)名以第一時(shí)間通過(guò)郵件發(fā)給我,通知我去下載。
一、編寫(xiě)思路:
1.爬蟲(chóng)腳本獲取當(dāng)日免費(fèi)書(shū)籍信息
2.把獲取到的書(shū)籍信息與數(shù)據(jù)庫(kù)中的已有信息作比較,如果書(shū)籍存在不做任何操作,書(shū)籍不存在,執(zhí)行插入數(shù)據(jù)庫(kù)的操作,把數(shù)據(jù)的信息存入MongoDB
3.執(zhí)行數(shù)據(jù)庫(kù)插入操作時(shí),把更新的數(shù)據(jù)以郵件的形式發(fā)送出來(lái)
4.用APScheduler調(diào)度框架完成python腳本調(diào)度
二、腳本的主要知識(shí)點(diǎn):
1.python簡(jiǎn)單爬蟲(chóng)
本次用到的模塊有urllib2用來(lái)抓取頁(yè)面,導(dǎo)入模塊如下:
import urllib2 from sgmllib import SGMLParser
urlopen()方法獲取網(wǎng)頁(yè)HTML源碼,都存儲(chǔ)在content中,listhref()類(lèi)主要的功能是解析HTML代碼,處理HTML類(lèi)型的半結(jié)構(gòu)化文檔。
content = urllib2.urlopen('http://sale.jd.com/act/yufbrhZtjx6JTV.html').read() listhref = ListHref() listhref.feed(content)
listhref()類(lèi)代碼可以在下面全部代碼中查詢到,這里只說(shuō)幾個(gè)關(guān)鍵點(diǎn):
listhref()類(lèi)繼承了SGMLParser 類(lèi)并重寫(xiě)了其中的內(nèi)部方法。SGMLParser 將HTML分解成有用的片段,比如開(kāi)始標(biāo)記和結(jié)束標(biāo)記。一旦成功地分解出某個(gè)數(shù)據(jù)為一個(gè)有用的片段,它會(huì)根據(jù)所發(fā)現(xiàn)的數(shù)據(jù),調(diào)用一個(gè)自身內(nèi)部的方法。為了使用這個(gè)分析器,您需要子類(lèi)化 SGMLParser類(lèi),并且重寫(xiě)父類(lèi)的這些方法。
SGMLParser 將 HTML 分析成不同類(lèi)數(shù)據(jù)及標(biāo)記,然后對(duì)每一類(lèi)調(diào)用單獨(dú)的方法:
開(kāi)始標(biāo)記 (Start_tag)
是一個(gè)開(kāi)始一個(gè)塊的 HTML 標(biāo)記,像 <html>,<head>,<body> , <pre> 等,或是一個(gè)獨(dú)一的標(biāo)記,象 <br> 或 <img> 等。本例當(dāng)它找到一個(gè)開(kāi)始標(biāo)記<a>,SGMLParser將查找名為 start_a或do_a的方法。如果找到了,SGMLParser會(huì)使用這個(gè)標(biāo)記的屬性列表來(lái)調(diào)用這個(gè)方法;否則,它用這個(gè)標(biāo)記的名字和屬性列表來(lái)調(diào)用unknown_starttag方法。
結(jié)束標(biāo)記 (End_tag)
是結(jié)束一個(gè)塊的HTML標(biāo)記,像 </html>,</head>,</body> 或 </pre> 等。本例中當(dāng)找到一個(gè)結(jié)束標(biāo)記時(shí),SGMLParser 將查找名為end_a的方法。如果找到,SGMLParser調(diào)用這個(gè)方法,否則它使用標(biāo)記的名字來(lái)調(diào)用unknown_endtag。
文本數(shù)據(jù)(Text data)
獲取文本塊,當(dāng)不滿足其它各類(lèi)別的任何標(biāo)記時(shí),調(diào)用handle_data獲取文本。
以下的幾類(lèi)在本文中沒(méi)有用到
字符引用 (Character reference)
用字符的十進(jìn)制或等同的十六進(jìn)制來(lái)表示的轉(zhuǎn)義字符,當(dāng)找到該字符,SGMLParser用字符調(diào)用 handle_charref 。
實(shí)體引用 (Entity reference)
HTML實(shí)體,像&ref,當(dāng)找到該實(shí)體,SGMLParser實(shí)體的名字調(diào)用handle_entityref。
注釋 (Comment)
HTML注釋, 包括在 <!-- ... -->之間。當(dāng)找到,SGMLParser用注釋內(nèi)容調(diào)用handle_comment。
處理指令 (Processing instruction)
HTML處理指令,包括在 <? ... > 之間。當(dāng)找到,SGMLParser用指令內(nèi)容調(diào) handle_pi。
聲明 (Declaration)
HTML聲明,如DOCTYPE,包括在 <! ... >之間。當(dāng)找到,SGMLParser用聲明內(nèi)容調(diào)用handle_decl。
具體的說(shuō)明參考API:http://docs.python.org/2/library/sgmllib.html?highlight=sgmlparser#sgmllib.SGMLParser
2.python操作MongoDB數(shù)據(jù)庫(kù)
首先要安裝python對(duì)mongoDB的驅(qū)動(dòng)PyMongo,下載地址:https://pypi.python.org/pypi/pymongo/2.5
導(dǎo)入模塊
import pymongo
連接數(shù)據(jù)庫(kù)服務(wù)器127.0.0.1和切換到所用數(shù)據(jù)庫(kù)mydatabase
mongoCon=pymongo.Connection(host="127.0.0.1",port=27017) db= mongoCon.mydatabase
查找數(shù)據(jù)庫(kù)相關(guān)書(shū)籍信息,book為查找的collection
bookInfo = db.book.find_one({"href":bookItem.href})
為數(shù)據(jù)庫(kù)插入書(shū)籍信息,python支持中文,但是對(duì)于中文的編碼和解碼還是比較復(fù)雜,相關(guān)解碼和編碼請(qǐng)參考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)
關(guān)于PyMongo請(qǐng)參考API文檔http://api.mongodb.org/python/2.0.1/
3.python發(fā)送郵件
導(dǎo)入郵件模塊
# Import smtplib for the actual sending function import smtplib from email.mime.text import MIMEText
"localhost"為郵件服務(wù)器地址
msg = MIMEText(context) #文本郵件的內(nèi)容
msg['Subject'] = sub #主題
msg['From'] = "my@vmail.cn" #發(fā)信人
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()
應(yīng)用文檔:http://docs.python.org/2/library/email.html?highlight=smtplib#
4.Python調(diào)度框架ApScheduler
下載地址https://pypi.python.org/pypi/APScheduler/2.1.0
官方文檔:http://pythonhosted.org/APScheduler/#faq
API:http://pythonhosted.org/APScheduler/genindex.html
安裝方法:下載之后解壓縮,然后執(zhí)行python setup.py install,導(dǎo)入模塊
from apscheduler.scheduler import Scheduler
ApScheduler配置比較簡(jiǎn)單,本例中只用到了add_interval_job方法,在每間隔一段時(shí)間后執(zhí)行任務(wù)腳本,本例中的間隔是30分鐘。可參考實(shí)例文章http://flykite.blog.51cto.com/4721239/832036
# Start the scheduler sched = Scheduler() sched.daemonic = False sched.add_interval_job(job,minutes=30) sched.start()
關(guān)于daemonic參數(shù):
apscheduler會(huì)創(chuàng)建一個(gè)線程,這個(gè)線程默認(rèn)是daemon=True,也就是默認(rèn)的是線程守護(hù)的。
在上面的代碼里面,要是不加上sched.daemonic=False的話,這個(gè)腳本就不會(huì)按時(shí)間運(yùn)行。
因?yàn)槟_本要是沒(méi)有sched.daemonic=False,它會(huì)創(chuàng)建一個(gè)守護(hù)線程。這個(gè)過(guò)程中,會(huì)創(chuàng)建scheduler的實(shí)例。但是由于腳本運(yùn)行速度很快,主線程mainthread會(huì)馬上結(jié)束,而此時(shí)定時(shí)任務(wù)的線程還沒(méi)來(lái)得及執(zhí)行,就跟隨主線程結(jié)束而結(jié)束了。(守護(hù)線程和主線程之間的關(guān)系決定的)。要讓腳本運(yùn)行正常,必須設(shè)置該腳本為非守護(hù)線程。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')=="限時(shí)免費(fèi)": 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()
- 自動(dòng)在Windows中運(yùn)行Python腳本并定時(shí)觸發(fā)功能實(shí)現(xiàn)
- 實(shí)現(xiàn)Windows下設(shè)置定時(shí)任務(wù)來(lái)運(yùn)行python腳本
- 如何給windows設(shè)置定時(shí)任務(wù)并運(yùn)行python腳本
- python 實(shí)現(xiàn)定時(shí)任務(wù)的四種方式
- python獲取指定時(shí)間段內(nèi)特定規(guī)律的日期列表
- python中用Scrapy實(shí)現(xiàn)定時(shí)爬蟲(chóng)的實(shí)例講解
- Python爬蟲(chóng)定時(shí)計(jì)劃任務(wù)的幾種常見(jiàn)方法(推薦)
- python實(shí)現(xiàn)定時(shí)發(fā)送郵件到指定郵箱
- python實(shí)現(xiàn)定時(shí)發(fā)送郵件
- python腳本定時(shí)發(fā)送郵件
- Python實(shí)現(xiàn)FTP文件定時(shí)自動(dòng)下載的步驟
- python爬蟲(chóng)調(diào)度器用法及實(shí)例代碼
- scrapy處理python爬蟲(chóng)調(diào)度詳解
- 簡(jiǎn)單的Python調(diào)度器Schedule詳解
- Python使用定時(shí)調(diào)度任務(wù)的方式
相關(guān)文章
Python中使用__new__實(shí)現(xiàn)單例模式并解析
單例模式是一個(gè)經(jīng)典設(shè)計(jì)模式,簡(jiǎn)要的說(shuō),一個(gè)類(lèi)的單例模式就是它只能被實(shí)例化一次,實(shí)例變量在第一次實(shí)例化時(shí)就已經(jīng)固定。 這篇文章主要介紹了Python中使用__new__實(shí)現(xiàn)單例模式并解析 ,需要的朋友可以參考下2019-06-06python 彈窗提示警告框MessageBox的實(shí)例
今天小編就為大家分享一篇python 彈窗提示警告框MessageBox的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-06-06Python基于identicon庫(kù)創(chuàng)建類(lèi)似Github上用的頭像功能
這篇文章主要介紹了Python基于identicon庫(kù)創(chuàng)建類(lèi)似Github上用的頭像功能,結(jié)合具體實(shí)例形式分析了identicon庫(kù)操作圖形的具體步驟與相關(guān)使用技巧,需要的朋友可以參考下2017-09-09Flask框架實(shí)現(xiàn)給視圖函數(shù)增加裝飾器操作示例
這篇文章主要介紹了Flask框架實(shí)現(xiàn)給視圖函數(shù)增加裝飾器操作,結(jié)合實(shí)例形式分析了flask框架視圖添加裝飾器的具體操作方法及相關(guān)注意事項(xiàng),需要的朋友可以參考下2018-07-07Python如何實(shí)現(xiàn)大型數(shù)組運(yùn)算(使用NumPy)
這篇文章主要介紹了Python如何實(shí)現(xiàn)大型數(shù)組運(yùn)算,文中講解非常細(xì)致,幫助大家更好的了解和學(xué)習(xí),感興趣的朋友可以了解下2020-07-07matplotlib grid()設(shè)置網(wǎng)格線外觀的實(shí)現(xiàn)
這篇文章主要介紹了matplotlib grid()設(shè)置網(wǎng)格線外觀的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-02-02python實(shí)現(xiàn)快遞價(jià)格查詢系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了python實(shí)現(xiàn)快遞價(jià)格查詢系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-03-03使用Python讀取和修改Excel文件(基于xlrd、xlwt和openpyxl模塊)
本文介紹一下使用Python對(duì)Excel文件的基本操作,包括使用xlrd模塊讀取excel文件,使用xlwt模塊將數(shù)據(jù)寫(xiě)入excel文件,使用openpyxl模塊讀取寫(xiě)入和修改excel文件,需要的朋友可以參考下2021-11-11