摘要:一直想做個這樣的爬蟲:定制自己的種子,爬取想要的數(shù)據(jù),做點力所能及的小分析。正好,這段時間寶寶出生,一邊陪寶寶和寶媽,一邊把自己做的這個豆瓣電影爬蟲的數(shù)據(jù)采集部分跑起來?,F(xiàn)在做一個概要的介紹和演示。動機(jī) 采集豆瓣電影數(shù)據(jù)包括電影詳情頁數(shù)據(jù)和電影的短評數(shù)據(jù)?! ‰娪霸斍轫撊缦聢D所示 需要保存這些詳情字段如導(dǎo)演、編劇、演員等還有圖中右下方的標(biāo)簽?! 《淘u頁面如下圖所示 需要保存的字段有短評所屬的
一直想做個這樣的爬蟲:定制自己的種子,爬取想要的數(shù)據(jù),做點力所能及的小分析。正好,這段時間寶寶出生,一邊陪寶寶和寶媽,一邊把自己做的這個豆瓣電影爬蟲的數(shù)據(jù)采集部分跑起來?,F(xiàn)在做一個概要的介紹和演示。
動機(jī)
采集豆瓣電影數(shù)據(jù)包括電影詳情頁數(shù)據(jù)和電影的短評數(shù)據(jù)。
電影詳情頁如下圖所示
需要保存這些詳情字段如導(dǎo)演、編劇、演員等還有圖中右下方的標(biāo)簽。
短評頁面如下圖所示
需要保存的字段有短評所屬的電影名稱,每條評論的詳細(xì)信息如評論人名稱、評論內(nèi)容等。
數(shù)據(jù)庫設(shè)計
有了如上的需求,需要設(shè)計表,其實很簡單,只需要一張電影詳情表movie和一張電影短評表comments,另外還需要一張存儲網(wǎng)頁提取的超鏈接的記錄表record。
movie表
movieId:主鍵,自增長
Name:電影名
Director:導(dǎo)演
Scenarist:編劇
Actors:主演
Type:類型
Country:制片國家/地區(qū)
Language:語言
releaseData: 上映日期
Runtime: 片長
ratingNum:豆瓣評分
Tags:標(biāo)簽
comments表
commentId:主鍵,自增長
commentInfo:評論內(nèi)容
commentAuthor:評論者
commentAuthorImgUrl:評論者頭像鏈接
commentVote:評論點贊數(shù)
commentForMovie:評論的電影名
recordId:鏈接record表,暫時未用到
record表
recordId:主鍵,自增長
URL:爬取解析的超鏈接
Crawled:是否被爬過
注意:數(shù)據(jù)庫設(shè)計是在不斷調(diào)整的,比如之前設(shè)計了一張tags表,用于存儲每部電影的標(biāo)簽,經(jīng)過調(diào)整發(fā)現(xiàn)直接放到movie中作為一個字段更加方便,又比如comments表中,commentForMovie是后來加上的,方便查找當(dāng)前的評論針對哪部電影。
使用的技術(shù)
語言:Java(語言是一門工具,網(wǎng)上用python,java,nodejs比較多)
數(shù)據(jù)庫:Mysql(輕便易用)
解析頁面:Jsoup(比較熟悉httpparser,雖然功能強(qiáng)大,但是稍顯繁瑣,這里用Jsoup,因為其為類javascript語法)、正則表達(dá)式(對于一些結(jié)構(gòu)比較奇怪的dom結(jié)構(gòu),采用了正則表達(dá)式的方式來提取信息,其實也可以用xpath,但是xpath極易受dom結(jié)構(gòu)變化而失效)
比如對于網(wǎng)頁源碼如下
<!DOCTYPE html> <html class="ua-windows ua-webkit"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <meta name="renderer" content="webkit"> <meta name="referrer" content="always"> <title> 肖申克的救贖 (豆瓣) </title> ..... <meta name="keywords" content="肖申克的救贖,The Shawshank Redemption,肖申克的救贖影評,劇情介紹,電影圖片,預(yù)告片,影訊,在線購票,論壇,肖申克的救贖在線觀看,肖申克的救贖高清,肖申克的救贖在線播放"> <meta name="description" content="肖申克的救贖電影簡介和劇情介紹,肖申克的救贖影評、圖片、預(yù)告片、影訊、論壇、在線購票、肖申克的救贖在線觀看、高清、在線播放"> ......... <div id="info"> <span ><span>導(dǎo)演</span>: <span><a href="/celebrity/1047973/" rel="v:directedBy">弗蘭克·德拉邦特</a></span></span><br/> <span ><span>編劇</span>: <span><a href="/celebrity/1047973/">弗蘭克·德拉邦特</a> / <a href="/celebrity/1049547/">斯蒂芬·金</a></span></span><br/> <span><span>主演</span>: <span><a href="/celebrity/1054521/" rel="v:starring">蒂姆·羅賓斯</a> / <a href="/celebrity/1054534/" rel="v:starring">摩根·弗里曼</a> / <a href="/celebrity/1041179/" rel="v:starring">鮑勃·岡頓</a> / <a href="/celebrity/1000095/" rel="v:starring">威廉姆·賽德勒</a> / <a href="/celebrity/1013817/" rel="v:starring">克蘭西·布朗</a> / <a href="/celebrity/1010612/" rel="v:starring">吉爾·貝羅斯</a> / <a href="/celebrity/1054892/" rel="v:starring">馬克·羅斯頓</a> / <a href="/celebrity/1027897/" rel="v:starring">詹姆斯·惠特摩</a> / <a href="/celebrity/1087302/" rel="v:starring">杰弗里·德曼</a> / <a href="/celebrity/1074035/" rel="v:starring">拉里·布蘭登伯格</a> / <a href="/celebrity/1099030/" rel="v:starring">尼爾·吉恩托利</a> / <a href="/celebrity/1343305/" rel="v:starring">布賴恩·利比</a> / <a href="/celebrity/1048222/" rel="v:starring">大衛(wèi)·普羅瓦爾</a> / <a href="/celebrity/1343306/" rel="v:starring">約瑟夫·勞格諾</a> / <a href="/celebrity/1315528/" rel="v:starring">祖德·塞克利拉</a></span></span><br/> <span>類型:</span> <span property="v:genre">劇情</span> / <span property="v:genre">犯罪</span><br/> <span>制片國家/地區(qū):</span> 美國<br/> <span>語言:</span> 英語<br/> <span>上映日期:</span> <span property="v:initialReleaseDate" content="1994-09-10(多倫多電影節(jié))">1994-09-10(多倫多電影節(jié))</span> / <span property="v:initialReleaseDate" content="1994-10-14(美國)">1994-10-14(美國)</span><br/> <span>片長:</span> <span property="v:runtime" content="142">142 分鐘</span><br/> <span>又名:</span> 月黑高飛(港) / 刺激1995(臺) / 地獄諾言 / 鐵窗歲月 / 消香克的救贖<br/> <span>IMDb鏈接:</span> <a href="http://www.imdb.com/title/tt0111161" target="_blank" rel="nofollow">tt0111161</a><br> </div> </div> <div id="interest_sectl"> <div class="rating_wrap clearbox" rel="v:rating"> <div>豆瓣評分</div> <div class="rating_self clearfix" typeof="v:Rating"> <strong class="ll rating_num" property="v:average">9.6</strong> <span property="v:best" content="10.0"></span> <div class="rating_right "> <div class="ll bigstar50"></div> <div> <a href="collections"><span property="v:votes">740373</span>人評價</a> </div> </div> </div> <span class="stars5 starstop" title="力薦"> 5星 </span> <div style="width:64px"></div> <span>81.5%</span> <br /> <span class="stars4 starstop" title="推薦"> 4星 </span> <div style="width:12px"></div> <span>16.2%</span> <br /> <span class="stars3 starstop" title="還行"> 3星 </span> <div style="width:1px"></div> <span>2.1%</span> <br /> <span class="stars2 starstop" title="較差"> 2星 </span> <div style="width:0px"></div> <span>0.1%</span> <br /> <span class="stars1 starstop" title="很差"> 1星 </span> <div style="width:0px"></div> <span>0.1%</span> <br /> </div> <div> 好于 <a href="/typerank?type_name=劇情&type=11&interval_id=100:90&action=">99% 劇情片</a><br/> 好于 <a href="/typerank?type_name=犯罪&type=3&interval_id=100:90&action=">99% 犯罪片</a><br/> </div> </div> </div> ......... <!-- sindar19a-docker--> <script>_SPLITTEST=''</script> </body> </html>
可以通過如下代碼來解析相應(yīng)字段(其中有用Jsoup和正則表達(dá)式的)
for (Element info : infos) { if (info.childNodeSize() > 0) { String key = info.getElementsByAttributeValue("class", "pl").text(); if ("導(dǎo)演".equals(key)) { movie.setDirector(info.getElementsByAttributeValue("class", "attrs").text()); } else if ("編劇".equals(key)) { movie.setScenarist(info.getElementsByAttributeValue("class", "attrs").text()); } else if ("主演".equals(key)) { movie.setActors(info.getElementsByAttributeValue("class", "attrs").text()); } else if ("類型:".equals(key)) { movie.setType(doc.getElementsByAttributeValue("property", "v:genre").text()); } else if ("制片國家/地區(qū):".equals(key)) { Pattern patternCountry = Pattern.compile(".制片國家/地區(qū):</span>.+[\\u4e00-\\u9fa5]+.+[\\u4e00-\\u9fa5]+\\s+<br>"); Matcher matcherCountry = patternCountry.matcher(doc.html()); if (matcherCountry.find()) { movie.setCountry(matcherCountry.group().split("</span>")[1].split("<br>")[0].trim());// for example: >制片國家/地區(qū):</span> 中國大陸 / 香港 <br> } } else if ("語言:".equals(key)) { Pattern patternLanguage = Pattern.compile(".語言:</span>.+[\\u4e00-\\u9fa5]+.+[\\u4e00-\\u9fa5]+\\s+<br>"); Matcher matcherLanguage = patternLanguage.matcher(doc.html()); if (matcherLanguage.find()) { movie.setLanguage(matcherLanguage.group().split("</span>")[1].split("<br>")[0].trim()); } } else if ("上映日期:".equals(key)) { movie.setReleaseDate(doc.getElementsByAttributeValue("property", "v:initialReleaseDate").text()); } else if ("片長:".equals(key)) { movie.setRuntime(doc.getElementsByAttributeValue("property", "v:runtime").text()); } } } movie.setTags(doc.getElementsByClass("tags-body").text()); movie.setName(doc.getElementsByAttributeValue("property", "v:itemreviewed").text()); movie.setRatingNum(doc.getElementsByAttributeValue("property", "v:average").text());
對于服務(wù)端返回不同狀態(tài)的http status,本程序?qū)τ谌?04,401,403,404等都采取了丟棄處理,不作解析。
效果展示
record表記錄
movie表記錄
comments表記錄
以上只是一個爬取數(shù)據(jù)的后臺實現(xiàn)雛形,還有很多細(xì)節(jié)的問題需要解決。后期可能會補(bǔ)上環(huán)境搭建以及遇到的問題和解決方法相關(guān)的文章。
程序爬取控制在豆瓣可接受范圍內(nèi),不會給豆瓣服務(wù)器帶來很大的壓力。