爲了家庭,14年辭職從杭州xxx回到湖南老家一個事業單位工做。平時工做量很少,閒暇時間本身搗騰搗騰技術。python一直聽聞使用起來比較簡單,上手起來也比較快,剛好朋友手頭有個項目,項目具體要求以下:
php
1、客戶有個APP,APP整天發一些和他們業務相關的新聞,新聞源頭都是來自於各大相關網站,可是因爲客戶本身平時時間緊湊,沒有不少閒暇時間去搜集各大網站他們想要的文章,因而就有了這個需求。客戶提供20多個網站網址,天天定時從各大網站抽取他們APP想要的文章。
html
2、抽取後的文章到APP後臺作一個審覈功能,審覈經過就直接轉載到他們APP。
python
3、文章須要歸類,不一樣文章類型最好在後臺歸類歸好,也方便他們發佈和挑選消息。
web
4、文章須要去重以避免影響其挑選速度
數據庫
以前工做時就看到同事用python工具爬蟲去爬取別人網站信息,因而就有了本身也學一下python的興趣。入門步驟開始,各類大牛繞道,別噴我這菜B了。windows
1、python安裝,直接進入到官網 https://www.python.org/downloads/ 點擊download你要的版本,我是下載了3.5最新版本。瀏覽器
2、要抓取別人網站內容,我在網上搜了一下解析html工具,使用 BeautifulSoup ,http://www.crummy.com/software/BeautifulSoup/bs4/download/ 這裏能夠下載,windows下也能夠直接下載tar.gz文件。網絡
3、安裝python 略多線程
4、安裝BeautifulSoup 模塊,用3.5.0就遇到這個坑,在py setup.py build / py setup.py install 以後 在命令行 執行 from bs4 import BeautifulSoup 會報錯,看提示說是 叫我從新安裝或者 run 2to3 將 bs4從2版本代碼轉換到3裏面也兼容,python在這方面作的比較好。因而我在python安裝目錄中找到 py ~/tools/scripts/2to3.py -w setup.py app
在安裝完成以後,必定要關閉掉命令窗口從新打開,要否則死也不會提示成功。
5、能夠編碼了,打開python編輯器,固然,在抓取別人網站前提先要了解一下這個網站頁面的結構,我是要抓取熱點新聞,因此從首頁下手,發現首頁熱點dom裏面有惟一標記 div的class =centertopnew 和 class=newslist ,因而我能夠直接從html中直接把這兩個節點提取出來,而後提取出帶超連接的標籤<a href=xxxx>xxx</a>,把文章名稱和文章超連接提取出來保存到本地文件中(剛入門,圖簡單直接寫到了文件,後面確定會移到數據庫中)
#coding=utf-8 import urllib.request import logging import urllib.parse import time from bs4 import BeautifulSoup ##日誌文件 LOG_PATH = "E:/pythonwork/spider/log/spider_log.log" ## 獲取頁面以後編碼轉換 PAGE_CODE = "GBK" ##爬蟲每爬一個頁面在該目錄下寫一個文件 FILE_BASE_PATH = "E:/pythonwork/spider/files/" ## 模擬火狐瀏覽器user-agent U_A = "Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)" HEADERS = {"User-Agent" : U_A } REQ_URL = "http://news.carnoc.com" ##初始化日誌類 logger=logging.getLogger() logger.setLevel(logging.INFO) handler=logging.FileHandler(LOG_PATH) logger.addHandler(handler) console = logging.StreamHandler() console.setLevel(logging.DEBUG) # 設置console日誌打印格式 formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s') console.setFormatter(formatter) logging.getLogger('').addHandler(console) values={'wd':'python', 'opt-webpage':'on', 'ie':'gbk'} url_values=urllib.parse.urlencode(values) url_values=url_values.encode(encoding='gb2312') ##構建request類 def getRequestClass(url,url_values): return urllib.request.Request(url,url_values,HEADERS) ##獲取網頁內容函數 def getHtml(req_url): try: response=urllib.request.urlopen(req_url) except urllib.HTTPError as e: print('Error code:',e.code) except URLError as e: print('Reason',e.reason) return response.read().decode("GBK") ##寫文件函數 def createNewFileAndWrite(file_name,content): file_obj = open(FILE_BASE_PATH+file_name+".txt","a") file_obj.write(content) file_obj.close() ##createNewFileAndWrite("test","中文測試內容") ##根據dom提取文章title和文章超連接 def getTitleAndUrl(newsHTML): resultList = [] for root in newsHTML: tempa = root.find_all("a") for dom in tempa: kv = [] href = dom.get("href") title = dom.string kv.append(title) kv.append(href) resultList.append(kv) return resultList ##循環dom list寫入文件中 def writeList(domList,partName): TIME_FORMAT = "%Y-%m-%d " now = time.strftime(TIME_FORMAT,time.localtime()) for dom in domList: createNewFileAndWrite(partName+"-"+now,dom[0]+"\1"+dom[1] + "\n") ## 入口函數 def start(): logging.warn("start get html content ...") htmlContent = getHtml(getRequestClass(REQ_URL,url_values)) logging.warn("get html success ...") soup = BeautifulSoup(htmlContent,"html.parser") logging.warn("write content to txt ...") ##熱點新聞 class=centertopnew writeList(getTitleAndUrl(soup.find_all("div",class_="centertopnew")),"centertopnew") ##各類新聞 class=newslist writeList(getTitleAndUrl(soup.find_all("div",class_="newslist")),"newslist") #入口 start()
上面文件保存,本地直接運行就會去網站裏面提取首頁的熱點文章,而後頁面裏每一塊新聞右上角都有「更多」,固然,這是我最想要的,由於在更多裏面我能夠提取更多我想要的文章。
6、根據首頁抓取文章,直接進一步抓取文章內容,而且作深度遞歸抓取,我暫時設置深度爲3,因而有了第二個代碼:
#coding=utf-8 ## 進入耳機頁面進行瘋狂的抓取,抓取深度爲3 ## 配置url規則 import re import urllib.request import logging import urllib.parse import time import traceback import os from bs4 import BeautifulSoup DEPTH = 3 ##日誌文件 LOG_PATH = "E:/pythonwork/spider/log/spider_log.log" ## 獲取頁面以後編碼轉換 PAGE_CODE = "GBK" ##爬蟲每爬一個頁面在該目錄下寫一個文件 FILE_BASE_PATH = "E:/pythonwork/spider/files/" ## 模擬火狐瀏覽器user-agent U_A = "Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)" HEADERS = {"User-Agent" : U_A } ##初始化日誌類 logger=logging.getLogger() logger.setLevel(logging.INFO) handler=logging.FileHandler(LOG_PATH) logger.addHandler(handler) console = logging.StreamHandler() console.setLevel(logging.DEBUG) # 設置console日誌打印格式 formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s') console.setFormatter(formatter) logging.getLogger('').addHandler(console) linkHashMap = {} values={'wd':'python', 'opt-webpage':'on', 'ie':'gbk'} url_values=urllib.parse.urlencode(values) url_values=url_values.encode(encoding='gb2312') reg = "^http://news.carnoc.com/{1}[]$" ##讀取抓取首頁後的文章 def getIndexResult(filepath): file_obj = open(filepath) try: all_content = file_obj.read() return all_content except Exception as ex: print(ex) finally: file_obj.close() p = re.compile(r"http://news.carnoc.com/(cache/)?(list/){1}(\w(/\w)*)+.html") ##深度挖掘數據,暫定爲深度爲3 def iteratorDrillData(content,depth): print("*******************當前深度:【"+str(depth)+"】*********************") if depth > 0 : contentlist = content.split("\n") for con in contentlist: kv = con.split("\1") try: if len(kv)>1 and kv[1] : if "http://news.carnoc.com/" not in kv[1]: kv[1] = "http://news.carnoc.com/"+kv[1] logging.info("請求"+kv[1]) if kv[1] in linkHashMap: break htmlContent = getHtml(getRequestClass(kv[1],url_values)) if htmlContent : alist = getTitleAndUrl(htmlContent) newsBlock = getPageNewsSoup(htmlContent) ## print(newsBlock) if newsBlock : fromSite = newsBlock.find_all(id="source_baidu") publishDate = newsBlock.find_all(id="pubtime_baidu") author = newsBlock.find_all(id="author_baidu") ##print(newsBlock[0]) newsText = newsBlock.find(id="newstext") if newsText : createNewFileAndWrite(mkdirBefore(kv[1][23:len(kv[1])]),str(newsText)) ##print(alist) for m in alist: if m and m[1] and p.match(m[1]): try: print(m) iteratorDrillData(m[0]+"\1"+m[1],depth-1) except: continue except : traceback.print_exc() continue def mkdirBefore(urlString): s = urlString.split("/") # lis = s[0:len(s)-1] # for i in range(0,len(s)-1): # if not os.path.exists(FILE_BASE_PATH+lis[i]): #os.mkdir(FILE_BASE_PATH+lis[i]) #print("建立目錄"+lis[i]) print("寫入目錄文件:"+"html/"+s[-1]) return "html/"+s[-1] def getRequestClass(url,url_values): linkHashMap[url] = 2 return urllib.request.Request(url,url_values,HEADERS) ##獲取網頁內容函數 def getHtml(req_url): try: response=urllib.request.urlopen(req_url) except : return None return response.read().decode("GBK") ##寫文件函數 def createNewFileAndWrite(file_name,content): file_obj = open(FILE_BASE_PATH+file_name+".txt","w+") file_obj.write(content) file_obj.close() ##根據dom提取文章title和文章超連接 def getTitleAndUrl(newsHTML): resultList = [] root = BeautifulSoup(newsHTML,"html.parser") tempa = root.find_all("a") for dom in tempa: kv = [] href = dom.get("href") title = dom.string kv.append(title) kv.append(href) resultList.append(kv) return resultList def getPageNewsSoup(htmlContent): return BeautifulSoup(htmlContent,"html.parser") ##http://news.carnoc.com/(cache/)?(list/){1}(\w)*(/\w)*.html iteratorDrillData(getIndexResult("E:/pythonwork/spider/files/centertopnew-2015-09-18 .txt"),3)
若是您想直接使用我代碼,注意要修改代碼中的文件目錄,先把目錄建好,須要在目錄裏面新建 log 和html目錄,log存放python日誌,html存放爬過來的文章內容。當初爲了快速上手目的,沒有把一切作成全自動的,由於要花一些時間來研究python的函數。
7、上面代碼意思是把第一個代碼匹配出來的連接請求進去,而後在請求返回結果中提取文章內容(也可能頁面沒有文章,多是「更多」,點進去就是文章列表)抽取文章內容也是根據dom的id來提取的。
8、在代碼中能夠看到我使用了遞歸,遞歸深度爲3,意思是把當前連接請求進去,而後把返回結果中有超鏈的連接摳出來繼續請求,相似於深度優先,其實我的以爲,在抓取網站時,廣度優先要好一點,由於在跑數據的時候,目錄一層一層的生成會看得比較清晰當前深度爬到了什麼程度!
因爲我本地網絡緣由,請求常常超時,爬了2小時才爬了幾百條數據,固然,我這種效率低是理所固然,後期確定會使用多線程甚至多進程。
因爲輸入數據和輸出數據均可以很相似 「文章標題」,「文章內容」,「文章超連接」,因此同一份代碼能夠繼續拿上一次跑出來的結果繼續遞歸來跑,這種相似廣度優先,先把當前頁面全部符合要求的超連接所有請求一邊,結果所有存起來,而後把所有結果歸總以後繼續用程序又所有跑一邊,又得出結果...如此以來能夠直接遞歸下去(若是文章數不多,而且頁面上沒有展現出各個文章之間的粘性,這種方式很那把文章爬完),我這種遞歸要求頁面和頁面之間有必定的關係,好比該頁面有些推薦了另一個頁面,這樣就會找到下一個頁面了。
8、去重
當前代碼去重是直接把url當作key ,每爬一個就放到字典裏面去,在爬一個url以前判斷一下我有沒有爬過,爬過就算了,寫文件這裏其實也至關於有去重 open('xxx','w') 會直接覆蓋掉重名的文件名。我這種去重只適合與單個網站去重,若是把20多個網站數據爬到一塊兒,那麼有可能會有問題,好比多個網站使用了phpwind,他們網站url規律都查不到,重複概率較高,不能用這個方式去重,要否則會覆蓋掉不該該覆蓋的數據。
9、文章歸類,還得去研究研究,經過文本內容自動歸類若是有作過這種東西的朋友能夠在評論裏面告知我,我也好學學!
文章寫的很差,見諒