通用論壇正文提取\論壇數(shù)據(jù)提取\論壇評論提取\論壇用戶信息爬蟲

本人長期出售超大量微博數(shù)據(jù)寿羞、旅游網(wǎng)站評論數(shù)據(jù)猖凛,并提供各種指定數(shù)據(jù)爬取服務,Message to YuboonaZhang@Yahoo.com绪穆。同時歡迎加入社交媒體數(shù)據(jù)交流群:99918768

背景

參加泰迪杯數(shù)據(jù)挖掘競賽辨泳,這次真的學習到了不少東西,最后差不多可以完成要求的內容玖院,準確率也還行菠红。總共的代碼难菌,算上中間的過程處理也不超過500行试溯,代碼思想也還比較簡單,主要是根據(jù)論壇的短文本特性和樓層之間內容的相似來完成的扔傅。(通俗點說就是去噪去噪去噪耍共,然后只留下相對有規(guī)律的日期烫饼,內容)

前期準備

  1. 軟件和開發(fā)環(huán)境: Pycharm,Python2.7试读,Linux系統(tǒng)

  2. 用的主要Python包: jieba杠纵, requests, BeautifulSoup钩骇, goose比藻, selenium, PhantomJS倘屹, pymongo等(部分軟件的安裝我前面的博客有介紹)

網(wǎng)頁預處理

首先因為網(wǎng)站很多是動態(tài)的银亲,直接用bs4是獲取不到有些信息的,所以我們使用selenium和phantomjs將文件保存在本地纽匙,然后再處理务蝠。

相關的代碼是

def save(baseUrl):
    driver = webdriver.PhantomJS()
    driver.get(baseUrl) # seconds
    try:
        element = WebDriverWait(driver, 10).until(isload(driver) is True)
    except Exception, e:
        print e
    finally:
        data = driver.page_source  # 取到加載js后的頁面content
    driver.quit()
    return data

由于網(wǎng)頁中存在著大量的噪音(廣告,圖片等)烛缔,首先我們需要將與我們所提取內容不一致的所有噪聲盡可能去除馏段。我們首先選擇將一些帶有典型噪聲意義的噪聲標簽去除,比如script等践瓷,方法我們選擇BeautifulSoup來完成院喜。

代碼大概是這樣

    for element in soup(text=lambda text: isinstance(text, Comment)):
        element.extract()

    [s.extract() for s in soup('script')]
    [s.extract() for s in soup('meta')]
    [s.extract() for s in soup('style')]
    [s.extract() for s in soup('link')]
    [s.extract() for s in soup('img')]
    [s.extract() for s in soup('input')]
    [s.extract() for s in soup('br')]
    [s.extract() for s in soup('li')]
    [s.extract() for s in soup('ul')]

    print (soup.prettify())

處理之后的網(wǎng)頁對比

之前
之后

可以看出網(wǎng)頁噪聲少了很多,但是還是不足以從這么多噪聲中提取出我們所要的內容

由于我們不需要標簽只需要標簽里面的文字,所以我們可以利用BeautifulSoup提取出文字內容再進行分析

for string in soup.stripped_strings:
    print(string)
    with open(os.path.join(os.getcwd())+"/data/3.txt", 'a') as f:
        f.writelines(string.encode('utf-8')+'\n')
去除噪聲標簽之后的信息

可以看出來還是非常雜亂晕翠,但是又是十分有規(guī)律的喷舀。我們可以發(fā)現(xiàn)每個樓層中的文本內容實質上都差不多,可以說重復的很多淋肾,而且都是一些特定的詞硫麻,比如: 直達樓層, 板凳,沙發(fā),等這類的詞,所以我們需要將這些詞刪掉然后再進行分析

我所用的方法是利用jieba分詞來對獲取的網(wǎng)頁文本進行分詞巫员,統(tǒng)計出出現(xiàn)詞頻最高的詞庶香,同時也是容易出現(xiàn)在噪聲文章中的詞語,代碼如下

import jieba.analyse

text = open(r"./data/get.txt", "r").read()

dic = {}
cut = jieba.cut_for_search(text)

for fc in cut:
    if fc in dic:
        dic[fc] += 1
    else:
        dic[fc] = 1
blog = jieba.analyse.extract_tags(text, topK=1000, withWeight=True)

for word_weight in blog:
    # print (word_weight[0].encode('utf-8'), dic.get(word_weight[0], 'not found'))
    with open('cut.txt', 'a') as f:
        f.writelines(word_weight[0].encode('utf-8') + "    " + str(dic.get(word_weight[0], 'not found')) + '\n')

統(tǒng)計出來然后經(jīng)過我們測試和篩選得出的停用詞有這些

回帖
積分
帖子
登錄
論壇
注冊
離線
時間
作者
簽到
主題
精華
客戶端
手機
下載
分享

目前統(tǒng)計的詞大約200左右简识。

然后還有去除重復文本的工作

# 去重函數(shù)
def remove_dup(items):
    pattern1 = re.compile(r'發(fā)表于')
    pattern2 = re.compile('\d{4}-\d{1,2}-\d{1,2} \d{2}:\d{2}:\d{2}')
    pattern3 = re.compile('\d{1,2}-\d{1,2} \d{2}:\d{2}')
    pattern4 = re.compile('\d{4}-\d{1,2}-\d{1,2} \d{2}:\d{2}')
    pattern5 = re.compile(r'[^0-9a-zA-Z]{7,}')

    # 用集合來作為容器赶掖,來做一部分的重復判斷依據(jù),另外的部分由匹配來做
    # yield用于將合適的文本用生成器得到迭代器七扰,這樣就進行了文本的刪除奢赂,在函數(shù)外面
    # 可以用函數(shù)進行文本的迭代
    seen = set()
    for item in items:
        match1 = pattern1.match(item)
        match2 = pattern2.match(item)
        match3 = pattern3.match(item)
        match4 = pattern4.match(item)
        match5 = pattern5.match(item)
        if item not in seen or match1 or match2 or match3 or match4 or match5:
            yield item
        seen.add(item)  # 向集合中加入item,集合會自動化刪除掉重復的項目

在經(jīng)過觀察處理后的網(wǎng)頁文本颈走,我們發(fā)現(xiàn)還有一項噪聲無法忽略膳灶,那就是純數(shù)字。因為網(wǎng)頁文本中有很多純數(shù)字但是又不重復,比如點贊數(shù)等轧钓,所以我準備用正則匹配出純數(shù)字然后刪除序厉。但是這樣就會出現(xiàn)問題...因為有些用戶名是純數(shù)字的,這樣我們會把用戶名刪掉的毕箍。為了解決這個問題我們使用保留字符數(shù)大于7的純數(shù)字弛房,這樣既刪除了大部分的沒用信息又盡可能的保留了用戶名

相關的代碼如下

st = []
    for stop_word in stop_words:
        st.append(stop_word.strip('\n'))
    t = tuple(st)
    # t,元組,和列表的區(qū)別是而柑,不能修改使用(文捶,,媒咳,粹排,),與【涩澡,顽耳,,】列表不同
    lines = []
    # 刪除停用詞和短數(shù)字實現(xiàn)
    for j in after_string:
        # 如果一行的開頭不是以停用詞開頭妙同,那么讀取這一行
        if not j.startswith(t):
            # 如何一行不全是數(shù)字斧抱,或者這行的數(shù)字數(shù)大于7(區(qū)別無關數(shù)字和數(shù)字用戶名)讀取這一行
            if not re.match('\d+$', j) or len(j) > 7:
                lines.append(j.strip())
                # 刪除所有空格并輸出
                print (j.strip())

處理之后的文本如下,規(guī)律十分明顯了

去除噪聲標簽之后的信息

接下來就是我們進行內容提取的時候了

內容提取

內容提取無非是找到評論塊渐溶,而評論塊在上面我們的圖中已經(jīng)十分清晰了,我們自然而然的想到根據(jù)日期來區(qū)分評論塊弄抬。經(jīng)過觀察茎辐,所有的論壇中日期的形式只有5種(目前只看到5種,當然后期可以加上)掂恕。我們可以用正則匹配出日期所在的行拖陆,根據(jù)兩個日期所在行數(shù)的中間所夾的就是評論內容和用戶名來完成我們的評論內容提取。

傳入我們處理后的文本然后就匹配出日期所在行數(shù)

# 匹配日期返回get_list
def match_date(lines):
    pattern1 = re.compile(r'發(fā)表于')
    pattern2 = re.compile('\d{4}-\d{1,2}-\d{1,2} \d{2}:\d{2}:\d{2}')
    pattern3 = re.compile('\d{1,2}-\d{1,2} \d{2}:\d{2}')
    pattern4 = re.compile('\d{4}-\d{1,2}-\d{1,2} \d{2}:\d{2}')
    pattern5 = re.compile(r'發(fā)表日期')

    pre_count = -1
    get_list = []

    # 匹配日期文本
    for string in lines:
        match1 = pattern1.match(string)
        match2 = pattern2.match(string)
        match3 = pattern3.match(string)
        match4 = pattern4.match(string)
        match5 = pattern5.match(string)
        pre_count += 1
        if match1 or match2 or match3 or match4 or match5:
            get_dic = {'count': pre_count, 'date': string}
            get_list.append(get_dic)

    # 返回的是匹配日期后的信息
    return get_list

因為有回帖和沒有回帖處理方式也不一樣所以我們需要分類進行討論懊亡。因為我們知道評論的內容是在兩個匹配日期的中間依啰,這樣就有一個問題就是最后一個評論的內容區(qū)域不好分。但是考慮到大部分的最后一個回帖都是一行我們可以暫取值為3(sub==3店枣,考慮一行評論和一行用戶名)速警,后來想到一種更為科學的方法,比如判斷后面幾行的文本密度鸯两,如果很小說明只有一行評論的可能性更大闷旧。

下面的代碼是獲取日期所在行數(shù)和兩個日期之間的行數(shù)差

# 返回my_count
def get_count(get_list):
    my_count = []
    date = []
    # 獲取時間所在行數(shù)
    for i in get_list:
        k, t = i.get('count'), i.get('date')
        my_count.append(k)
        date.append(t)
    if len(get_list) > 1:
        # 最后一行暫時取3
        my_count.append(my_count[-1] + 3)
        return my_count
    else:
        return my_count

# 獲取兩個時間所在的行數(shù)差
def get_sub(my_count):
    sub = []
    for i in range(len(my_count) - 1):
        sub.append(my_count[i + 1] - my_count[i])
    return sub

接下來就要分類討論了

  1. 如果只有樓主沒有評論(即my——count==1),這個時候我們可以使用開源的正文提取軟件goose來提取正文钧唐。

  2. 如果有評論我們就需要根據(jù)sub的值來進行分類如果sub==2占多數(shù)(或者說比sub==3)占的多忙灼,那么我們就認為可能是用戶名被刪掉,刪掉的原因有很多,比如去重的時候有人在樓中樓回復了導致用戶名重復被刪除该园,有可能該網(wǎng)站的標簽比較特殊用戶名在去標簽的時候刪除等酸舍,情況比較復雜且出現(xiàn)的頻率不太高,暫未考慮里初。何況不影響我們提取評論內容啃勉,只需分類出來考慮就行

<font color=#FF0000 size=4 face="黑體">
注意:下面余弦相似度這個是我開始的時候想多了!大部分情況就是:日期-評論-用戶名青瀑,后來我沒有考慮余弦相似度分類璧亮,代碼少了,精度也沒有下降斥难。這里不刪是想留下一個思考的過程枝嘶。代碼看看就好,最后有修改后的源碼哑诊。
</font>

  1. 還有就是最常見的內容群扶,就是sub==3占多數(shù)的情況。因為大部分的評論都是一行文本镀裤,所以我們需要考慮的的是sub==3的時候獲取的評論文本在哪一行竞阐。通俗來說就是這三行的內容是日期-評論-用戶名,還是日期-用戶名-評論呢暑劝?雖然大部分是第一種情況骆莹,但是第二種情況我們也不能忽略。怎么判斷這兩種情況呢担猛?這確實讓我思考了很長一段時間幕垦,后來想到可以用余弦相似度來解決這個問題.科普余弦相似度可以看這里。簡單來說就是用戶名的長度都是相似的傅联,但是評論的內容長度差異就非常大了先改。比如用戶名長度都是7個字符左右,但是評論的長度可以數(shù)百蒸走,也可以只有一個仇奶。所以我們可以兩兩比較余弦相似度,然后取平均比驻,相似度大的就是用戶名了该溯。這樣我們就可以區(qū)分出評論內容進行提取了!這就是主要的思想嫁艇。剩下的就是代碼的實現(xiàn)了朗伶。

簡單貼一下相關的代碼

# 利用goose獲取正文內容
def goose_content(my_count, lines, my_url):
    g = Goose({'stopwords_class': StopWordsChinese})
    content_1 = g.extract(url=my_url)
    host = {}
    my_list = []
    host['content'] = content_1.cleaned_text
    host['date'] = lines[my_count[0]]
    host['title'] = get_title(my_url)
    result = {"post": host, "replys": my_list}
    SpiderBBS_info.insert(result)

# 計算余弦相似度函數(shù)
def cos_dist(a, b):
    if len(a) != len(b):
        return None
    part_up = 0.0
    a_sq = 0.0
    b_sq = 0.0
    for a1, b1 in zip(a, b):
        part_up += a1 * b1
        a_sq += a1 ** 2
        b_sq += b1 ** 2
    part_down = math.sqrt(a_sq * b_sq)
    if part_down == 0.0:
        return None
    else:
        return part_up / part_down

# 判斷評論內容在哪一行(可能在3行評論塊的中間,可能在三行評論塊的最后)
def get_3_comment(my_count, lines):
    get_pd_1 = []
    get_pd_2 = []
    # 如果間隔為3取出所在行的文本長度
    test_sat_1 = []
    test_sat_2 = []
    for num in range(len(my_count)-1):
        if my_count[num+1] - 3 == my_count[num]:
            pd_1 = (len(lines[my_count[num]]), len(lines[my_count[num]+2]))
            get_pd_1.append(pd_1)
            pd_2 = (len(lines[my_count[num]]), len(lines[my_count[num]+1]))
            get_pd_2.append(pd_2)

    for i_cos in range(len(get_pd_1)-1):
        for j_cos in range(i_cos+1, len(get_pd_1)):
            # 計算文本余弦相似度
            test_sat_1.append(cos_dist(get_pd_1[j_cos], get_pd_1[i_cos]))
            test_sat_2.append(cos_dist(get_pd_2[j_cos], get_pd_2[i_cos]))

    # 計算余弦相似度的平均值
    get_mean_1 = numpy.array(test_sat_1)
    print (get_mean_1.mean())
    get_mean_2 = numpy.array(test_sat_2)
    print (get_mean_2.mean())

    # 比較大小返回是否應該按
    if get_mean_1.mean() >= get_mean_2.mean():
        return 1
    elif get_mean_1.mean() < get_mean_2.mean():
        return 2

# 獲取評論內容
def solve__3(num, my_count, sub, lines, my_url):
    # 如果get_3_comment()返回的值是1步咪,那么說明最后一行是用戶名的可能性更大论皆,否則第一行是用戶名的可能性更大
    if num == 1:
        host = {}
        my_list = []
        host['content'] = ''.join(lines[my_count[0]+1: my_count[1]+sub[0]-1])
        host['date'] = lines[my_count[0]]
        host['title'] = get_title(my_url)
        for use in range(1, len(my_count)-1):
            pl = {'content': ''.join(lines[my_count[use] + 1:my_count[use + 1] - 1]), 'date': lines[my_count[use]],
                  'title': get_title(my_url)}
            my_list.append(pl)

        result = {"post": host, "replys": my_list}
        SpiderBBS_info.insert(result)

    if num == 2:
        host = {}
        my_list = []
        host['content'] = ''.join(lines[my_count[0]+2: my_count[1]+sub[0]])
        host['date'] = lines[my_count[0]]
        host['title'] = get_title(my_url)
        for use in range(1, len(my_count) - 1):
            pl = {'content': ''.join(lines[my_count[use] + 2:my_count[use + 1]]), 'date': lines[my_count[use]],
                  'title': get_title(my_url)}
            my_list.append(pl)

        result = {"post": host, "replys": my_list}
        SpiderBBS_info.insert(result)


展望

提取的準確率應該要分析更多的bbs網(wǎng)站,優(yōu)化刪除重復詞(太粗暴),優(yōu)化停用詞点晴,針對短文本沒回復情況的優(yōu)化感凤,準確提取樓主的用戶名等,無奈時間太緊無法進一步優(yōu)化粒督。才疏學淺陪竿,剛學了幾個月python,代碼難免有不合理的地方屠橄,望各位提出寶貴意見族跛。

個人博客

8aoy1.cn

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市锐墙,隨后出現(xiàn)的幾起案子礁哄,更是在濱河造成了極大的恐慌,老刑警劉巖溪北,帶你破解...
    沈念sama閱讀 216,692評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件桐绒,死亡現(xiàn)場離奇詭異,居然都是意外死亡之拨,警方通過查閱死者的電腦和手機茉继,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蚀乔,“玉大人烁竭,你說我怎么就攤上這事〖酰” “怎么了颖变?”我有些...
    開封第一講書人閱讀 162,995評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長听想。 經(jīng)常有香客問我,道長马胧,這世上最難降的妖魔是什么汉买? 我笑而不...
    開封第一講書人閱讀 58,223評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮佩脊,結果婚禮上蛙粘,老公的妹妹穿的比我還像新娘。我一直安慰自己威彰,他們只是感情好出牧,可當我...
    茶點故事閱讀 67,245評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著歇盼,像睡著了一般舔痕。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,208評論 1 299
  • 那天伯复,我揣著相機與錄音慨代,去河邊找鬼。 笑死啸如,一個胖子當著我的面吹牛侍匙,可吹牛的內容都是我干的。 我是一名探鬼主播叮雳,決...
    沈念sama閱讀 40,091評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼想暗,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了帘不?” 一聲冷哼從身側響起说莫,我...
    開封第一講書人閱讀 38,929評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎厌均,沒想到半個月后唬滑,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,346評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡棺弊,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,570評論 2 333
  • 正文 我和宋清朗相戀三年晶密,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片模她。...
    茶點故事閱讀 39,739評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡稻艰,死狀恐怖,靈堂內的尸體忽然破棺而出侈净,到底是詐尸還是另有隱情尊勿,我是刑警寧澤,帶...
    沈念sama閱讀 35,437評論 5 344
  • 正文 年R本政府宣布畜侦,位于F島的核電站元扔,受9級特大地震影響,放射性物質發(fā)生泄漏旋膳。R本人自食惡果不足惜澎语,卻給世界環(huán)境...
    茶點故事閱讀 41,037評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望验懊。 院中可真熱鬧擅羞,春花似錦、人聲如沸义图。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,677評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽碱工。三九已至娃承,卻和暖如春奏夫,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背草慧。 一陣腳步聲響...
    開封第一講書人閱讀 32,833評論 1 269
  • 我被黑心中介騙來泰國打工桶蛔, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人漫谷。 一個月前我還...
    沈念sama閱讀 47,760評論 2 369
  • 正文 我出身青樓仔雷,卻偏偏與公主長得像,于是被迫代替她去往敵國和親舔示。 傳聞我的和親對象是個殘疾皇子碟婆,可洞房花燭夜當晚...
    茶點故事閱讀 44,647評論 2 354

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,082評論 25 707
  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn)惕稻,斷路器竖共,智...
    卡卡羅2017閱讀 134,652評論 18 139
  • 抓取京東商城商品評論信息,并對這些評論信息進行分析和可視化俺祠。下面是要抓取的商品信息公给,一款女士文胸。這個商品共有紅色...
    _hahaha閱讀 6,134評論 2 7
  • 想來許久不見,定會甚是想念蔫缸? 想來這么久了又見腿准,仍然有些害怕 今日有些犯困,聽著歌就睡著了拾碌,后來大約播了首歡快的歌...
    洛云端閱讀 185評論 2 0
  • 摘抄于百度 100 Continue 初始的請求已經(jīng)接受吐葱,客戶應當繼續(xù)發(fā)送請求的其余部分。(HTTP 1.1新)1...
    麒緣伊聲閱讀 456評論 0 0