比如簡(jiǎn)書:
Paste_Image.png
我們來(lái)寫個(gè)程序祭阀,爬取簡(jiǎn)書網(wǎng)站隨便一個(gè)作者的所有文章盟榴,再對(duì)其所有文章進(jìn)行分詞統(tǒng)計(jì)
程序運(yùn)行統(tǒng)計(jì)的結(jié)果見(jiàn)文章:
我統(tǒng)計(jì)了彭小六簡(jiǎn)書360篇文章中使用的詞語(yǔ)
需要的Python包
包名 | 作用 |
---|---|
selenium | 用于和phantomjs合作模擬瀏覽器訪問(wèn)網(wǎng)頁(yè) |
lxml | 用于對(duì)html頁(yè)面的解析倔监,提取數(shù)據(jù) |
jieba | 用于對(duì)文章正文分詞 |
tld | 解析url, 比如提取domain |
還需要下載 phantomjs义矛,selenium配合phantomjs的使用代碼中有體現(xiàn)
下載地址: http://phantomjs.org/
下面代碼中派诬,由于使用文件保存數(shù)據(jù),而沒(méi)有使用數(shù)據(jù)庫(kù)保存數(shù)據(jù)圈纺,所以代碼量比較多秦忿,其中主要代碼并不多
直接上代碼####
# -*-coding:utf-8-*-
import json
import os, sys
from random import randint
from collections import Counter
import jieba
from lxml import etree
from selenium import webdriver
import time
from tld import get_tld
path = os.path.abspath(os.path.dirname(__file__))
class Spider():
'''
獲取簡(jiǎn)書作者的全部文章頁(yè)面,并解析
'''
def __init__(self, start_url):
'''
我這里使用文件保存數(shù)據(jù)蛾娶,沒(méi)有使用數(shù)據(jù)庫(kù)保存數(shù)據(jù)
所有需要初始化文件保存路徑
使用本程序的你可以把文件保存改成數(shù)據(jù)庫(kù)保存,建議使用nosql方便保存
start_url:作者文章列表頁(yè)面灯谣,比如http://www.reibang.com/u/65fd4e5d930d
:return:
'''
self.start_url = start_url
res = get_tld(self.start_url, as_object=True, fix_protocol=True)
self.domain = "{}.{}".format(res.subdomain, res.tld)
self.user_id = self.start_url.split("/")[-1]
# 保存作者文章列表html頁(yè)面
post_list_dir = '{}/post-list'.format(path)
self.post_lists_html = '{}/post_list_{}.html'.format(post_list_dir, self.user_id)
# 保存作者所有文章的url
self.post_lists_urls = '{}/urls_{}.dat'.format(post_list_dir, self.user_id)
# 保存文章原始網(wǎng)頁(yè):
self.posts_html_dir = '{}/post-html/{}'.format(path, self.user_id)
# 保存文章解析后的內(nèi)容:
self.posts_data_dir = '{}/post-data/{}'.format(path,self.user_id)
# 保存文章統(tǒng)計(jì)后的結(jié)果:
self.result_dir = '{}/result'.format(path)
self.executable_path='{}/phantomjs-2.1.1-linux-x86_64/bin/phantomjs'.format(path)
# mkdir
if not os.path.exists(self.posts_html_dir):
os.makedirs(self.posts_html_dir)
if not os.path.exists(self.posts_data_dir):
os.makedirs(self.posts_data_dir)
if not os.path.exists(post_list_dir):
os.makedirs(post_list_dir)
if not os.path.exists(self.result_dir):
os.makedirs(self.result_dir)
# 網(wǎng)上隨筆找的免費(fèi)代理ip
self.ips = ['61.167.222.17:808','58.212.121.72:8998', '111.1.3.36:8000', '125.117.133.74:9000']
def post_list_page(self):
'''
獲取文章列表頁(yè)面,以及文章鏈接
:return:
'''
obj = webdriver.PhantomJS(executable_path=self.executable_path)
obj.set_page_load_timeout(30)
obj.maximize_window()
# 隨機(jī)一個(gè)代理ip
ip_num = len(self.ips)
ip = self.ips[randint(0,ip_num-1)]
obj.http_proxy = ip
obj.get(self.start_url)
# 文章總數(shù)量
sel = etree.HTML(obj.page_source)
r = sel.xpath("http://div[@class='main-top']//div[@class='info']//li[3]//p//text()")
if r:
crawl_post_n = int(r[0])
else:
print("[Error] 提取文章總書的xpath不正確")
sys.exit()
n = crawl_post_n/9
i = 1
while n:
t = randint(2,5)
time.sleep(t)
js = "var q=document.body.scrollTop=100000"
# 頁(yè)面一直下滾
obj.execute_script(js)
n -= 1
i += 1
# 然后把作者文章列表頁(yè)面的html(保存到數(shù)據(jù)庫(kù)蛔琅,或文本保存)
of = open(self.post_lists_html, "w")
of.write(obj.page_source)
of.close()
# 我們也順便把作者所有的文章鏈接提取出來(lái)(保存到數(shù)據(jù)庫(kù)胎许,或文本保存)
of = open(self.post_lists_urls, "w")
sel = etree.HTML(obj.page_source)
results = sel.xpath("http://div[@id='list-container']//li//a[@class='title']/@href")
for result in results:
of.write("http://{}{}".format(self.domain, result.strip()))
of.write("\n")
of.close()
def posts_html(self):
'''
獲取文章頁(yè)面html
:return:
'''
of = open(self.post_lists_urls)
urls = of.readlines()
ip_num = len(self.ips)
obj = webdriver.PhantomJS(executable_path=self.executable_path)
obj.set_page_load_timeout(10)
obj.maximize_window()
for url in urls:
# 隨機(jī)一個(gè)代理ip
ip = self.ips[randint(0,ip_num-1)]
obj.http_proxy = ip
url = url.strip()
print("代理ip:{}".format(ip))
print("網(wǎng)頁(yè):{}".format(url))
try:
obj.get(url)
except:
print("Error:{}".format(url))
post_id = url.split("/")[-1]
of = open("{}/{}_{}.html".format(self.posts_html_dir, obj.title, post_id), "w")
of.write(obj.page_source)
of.close()
t = randint(1,5)
time.sleep(t)
def page_parsing(self):
'''
html解析
:return:
'''
# 只獲取匹配的第一個(gè)
xpath_rule_0 ={
"author":"http://div[@class='author']//span[@class='name']//text()", # 作者名字
"author_tag":"http://div[@class='author']//span[@class='tag']//text()",# 作者標(biāo)簽
"postdate":"http://div[@class='author']//span[@class='publish-time']//text()", # 發(fā)布時(shí)間
"word_num":"http://div[@class='author']//span[@class='wordage']//text()",#字?jǐn)?shù)
"notebook":"http://div[@class='show-foot']//a[@class='notebook']/span/text()",#文章屬于的目錄
"title":"http://div[@class='article']/h1[@class='title']//text()",#文章標(biāo)題
}
# 獲取匹配的所有,并拼接成一個(gè)字符串的
xpath_rule_all_tostr ={
"content":"http://div[@class='show-content']//text()",#正文
}
# 獲取匹配的所有,保存數(shù)組形式
xpath_rule_all ={
"collection":"http://div[@class='include-collection']//a[@class='item']//text()",#收入文章的專題
}
# 遍歷所有文章的html文件,如果保存在數(shù)據(jù)庫(kù)的則直接查詢出來(lái)
list_dir = os.listdir(self.posts_html_dir)
for file in list_dir:
file = "{}/{}".format(self.posts_html_dir, file)
if os.path.isfile(file):
of = open(file)
html = of.read()
sel = etree.HTML(html)
of.close()
# 解析
post_id = file.split("_")[-1].strip(".html")
doc = {'url':'http://{}/p/{}'.format(self.domain,post_id)}
for k,rule in xpath_rule_0.items():
results = sel.xpath(rule)
if results:
doc[k] = results[0]
else:
doc[k] = None
for k,rule in xpath_rule_all_tostr.items():
results = sel.xpath(rule)
if results:
doc[k] = ""
for result in results:
if result.strip():
doc[k] = "{}{}".format(doc[k], result)
else:
doc[k] = None
for k,rule in xpath_rule_all.items():
results = sel.xpath(rule)
if results:
doc[k] = results
else:
doc[k] = None
if doc["word_num"]:
doc["word_num"] = int(doc["word_num"].strip('字?jǐn)?shù)').strip())
else:
doc["word_num"] = 0
# 保存到數(shù)據(jù)庫(kù)或者文件中
of = open("{}/{}.json".format(self.posts_data_dir, post_id), "w")
of.write(json.dumps(doc))
of.close()
def statistics(self):
'''
分開對(duì)每篇文章的進(jìn)行分詞統(tǒng)計(jì)罗售,也統(tǒng)計(jì)全部文章分詞
:return:
'''
# 遍歷所有文章的html文件辜窑,如果保存在數(shù)據(jù)庫(kù)的則直接查詢出來(lái)
word_sum = {} #正文全部詞語(yǔ)統(tǒng)計(jì)
title_word_sum = {} #標(biāo)題全部詞語(yǔ)統(tǒng)計(jì)
post_word_cnt_list = [] #每篇文章使用的詞匯數(shù)量
# 正文統(tǒng)計(jì)數(shù)據(jù)保存
list_dir = os.listdir(self.posts_data_dir)
for file in list_dir:
file = "{}/{}".format(self.posts_data_dir, file)
if os.path.isfile(file):
of = open(file)
str = of.read()
doc = json.loads(str)
# 正文統(tǒng)計(jì):精確模式,默認(rèn)hi精確模式,所以可以不指定cut_all=False
words = jieba.cut(doc["content"], cut_all=False)
data = dict(Counter(words))
data = sorted(data.iteritems(), key=lambda d: d[1], reverse=True)
word_cnt = 0
for w in data:
# 只統(tǒng)計(jì)超過(guò)1個(gè)字的詞語(yǔ)
if len(w[0]) < 2:
continue
# 統(tǒng)計(jì)到全部文章詞語(yǔ)中
if w[0] in word_sum:
word_sum[w[0]]["cnt"] += w[1]
word_sum[w[0]]["post_cnt"] += 1
else:
word_sum[w[0]] = {}
word_sum[w[0]]["cnt"] = w[1]
word_sum[w[0]]["post_cnt"] = 1
word_cnt += 1
post_word_cnt_list.append((word_cnt,
doc["postdate"],
doc["title"],
doc["url"]))
# 標(biāo)題統(tǒng)計(jì):精確模式,默認(rèn)hi精確模式寨躁,所以可以不指定cut_all=False
words = jieba.cut(doc["title"], cut_all=False)
data = dict(Counter(words))
data = sorted(data.iteritems(), key=lambda d: d[1], reverse=True)
for w in data:
# 只統(tǒng)計(jì)超過(guò)1個(gè)字的詞語(yǔ)
if len(w[0]) < 2:
continue
# 統(tǒng)計(jì)到全部文章詞語(yǔ)中
if w[0] in title_word_sum:
title_word_sum[w[0]]["cnt"] += w[1]
title_word_sum[w[0]]["post_cnt"] += 1
else:
title_word_sum[w[0]] = {}
title_word_sum[w[0]]["cnt"] = w[1]
title_word_sum[w[0]]["post_cnt"] = 1
post_word_cnt_list = sorted(post_word_cnt_list, key=lambda d: d[0], reverse=True)
wf = open("{}/content_statis_{}.dat".format(self.result_dir, self.user_id), "w")
wf.write("| 詞語(yǔ) | 發(fā)布日期 | 標(biāo)題 | 鏈接 |\n")
for pw in post_word_cnt_list:
wf.write("| {} | {} | {}| {}|\n".format(pw[0],pw[1],pw[2],pw[3]))
wf.close()
# 全部文章正文各詞語(yǔ) 按使用次數(shù) 統(tǒng)計(jì)結(jié)果
wf = open("{}/content_statis_sum_use-num_{}.dat".format(self.result_dir, self.user_id), "w")
word_sum_t = sorted(word_sum.iteritems(), key=lambda d: d[1]['cnt'], reverse=True)
wf.write("| 分詞 | 使用次數(shù) | 使用的文章數(shù)量|\n")
for w in word_sum_t:
wf.write("| {} | {} | {}|\n".format(w[0], w[1]["cnt"], w[1]["post_cnt"]))
wf.close()
# 全部文章正文各詞語(yǔ) 按使用文章篇數(shù) 統(tǒng)計(jì)結(jié)果
wf = open("{}/content_statis_sum_post-num_{}.dat".format(self.result_dir, self.user_id), "w")
word_sum_t = sorted(word_sum.iteritems(), key=lambda d: d[1]['post_cnt'], reverse=True)
wf.write("| 分詞 | 使用的文章數(shù)量 | 使用次數(shù) |\n")
for w in word_sum_t:
wf.write("| {} | {} | {}|\n".format(w[0], w[1]["post_cnt"], w[1]["cnt"]))
wf.close()
# 全部文章title各詞語(yǔ) 按使用次數(shù) 統(tǒng)計(jì)結(jié)果
wf = open("{}/title_statis_sum_use-num_{}.dat".format(self.result_dir,self.user_id), "w")
title_word_sum_t = sorted(title_word_sum.iteritems(), key=lambda d: d[1]['cnt'], reverse=True)
wf.write("| 分詞 | 使用次數(shù) | 使用的文章數(shù)量|\n")
for w in title_word_sum_t:
wf.write("| {} | {} | {}|\n".format(w[0], w[1]["cnt"], w[1]["post_cnt"]))
wf.close()
# 全部文章title各詞語(yǔ) 按使用次數(shù) 統(tǒng)計(jì)結(jié)果
wf = open("{}/title_statis_sum_post-num_{}.dat".format(self.result_dir, self.user_id), "w")
title_word_sum_t = sorted(title_word_sum.iteritems(), key=lambda d: d[1]['post_cnt'], reverse=True)
wf.write("| 分詞 | 使用的文章數(shù)量 | 使用次數(shù) |\n")
for w in title_word_sum_t:
wf.write("| {} | {} | {}|\n".format(w[0], w[1]["post_cnt"], w[1]["cnt"]))
wf.close()
print("一共統(tǒng)計(jì)文章:{} 篇".format(len(list_dir)))
print("所有正文-使用了2字及以上詞語(yǔ):{} 個(gè)".format(len(word_sum_t)))
print("所有標(biāo)題-使用了2字及以上詞語(yǔ):{} 個(gè)".format(len(title_word_sum_t)))
if __name__ == '__main__':
sp = Spider(start_url="http://www.reibang.com/u/65fd4e5d930d")
print("獲取作者文章列表頁(yè)面...")
sp.post_list_page()
print("獲取作者所有文章頁(yè)面...")
#sp.posts_html()
print("解析作者所有文章頁(yè)面...")
#sp.page_parsing()
print("簡(jiǎn)單統(tǒng)計(jì)分析文章詞匯...")
#sp.statistics()
程序運(yùn)行統(tǒng)計(jì)的結(jié)果見(jiàn)文章: 我統(tǒng)計(jì)了彭小六簡(jiǎn)書360篇文章中使用的詞語(yǔ)