一、前言
博客,這東西寫起來是真的麻煩。博主語文特不好,什麼語句、語義不通之類的,是常有的。請務必不要在意。如果,有什麼問題可以在底下留言,或者私信我索要聯繫方式。另外,博主常年混跡java吧,在那肯定能找到我,貼吧ID:ZSsanguosha 。 轉載,保留“全屍”就可以了,標明出處和作者,,其餘隨意。以上
博主郵箱的綁定了數據庫預警系統,一響就代表採集系統崩了,已經響出陰影了。所以別問我要郵箱。
最後,本篇講的是微博爬蟲的基礎實現,適用範圍:小規模使用
二、原理
微博有一個“Sina Visitor System”(新浪訪客系統),如果沒有專門的微博cookie,所有請求都會被這個系統攔截。反過來說,只要獲取到這個cookie,就能爬取到微博的頁面。
類似的教程網上有很多,但其實有很多教程,一些細節(注意事項)都沒有講清楚,所以,我特意整理一下。
三、實現
爬蟲,博主推薦用python實現,不過在這裏,博主選用java給大家演示。因爲博主以前已經用java實現過一遍,ctrl+cv就行,實在不想再寫一遍。
接下來,就是實戰。這次選用的是微博的榜單頁面(https://d.weibo.com/1087030002_2975_2017_0)
獲取cookie: 先登錄微博。然後谷歌瀏覽器,F12打開開發者,ctrl+R刷新請求,找到當前頁面的請求,複製cookie。
1、依賴
本次選用的是 請求工具是httpclient,解析頁面是 jsoup
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.10.1</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.5</version>
</dependency>
2、模擬請求,獲取html
創建get請求,設置請求頭,模擬真實用戶。將上圖的請求頭信息,複製進來,即可
微博的大部分頁面都是get請求,這些請求頭其實只要設置"user_agent"和“host”還有“cookie” 就可以了,其餘可要不可不要
csdn的排版不怎麼熟悉,可能是因爲太長了,實在太醜,所以有些代碼是截圖的
httpHost 是用來設置代理的,不需要可以填入null
private RequestConfig getRequestConfig() {
return RequestConfig.custom().setSocketTimeout(3000).setConnectTimeout(3000)
.setConnectionRequestTimeout(3000)
.setProxy(httpHost).build();
}
博主在中間加了一步驗證。主要是因爲,微博爬蟲,時不時會因爲各種問題出現各種錯誤以及各種錯誤界面,光用響應代碼是不好控制的,所以加一步響應內容的校驗。
for循環是爲了排除網絡問題導致的爬取失敗
“empty_con clearfix”是目標頁面的某個標籤id,“follow_item S_line2”是某個錯誤頁面的
private String get(HttpGet httpGet) {
//設置返回內容的檢測邏輯
Predicate<String> predicate = s -> (s == null || "".equals(s))
|| s.contains("empty_con clearfix") || !s.contains("follow_item S_line2");
return get(httpGet, predicate);
}
private String get(HttpGet httpGet, Predicate<String> predicate) {
//最多重試5次
for(int i=0;i<5;i++) {
try {
CloseableHttpResponse response = httpclient.execute(httpGet);
HttpEntity httpEntity = response.getEntity();
String responseContent = getResponseContent(httpEntity);
//雙重判斷
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK
&& !predicate.test(responseContent))
{
return responseContent;
}
}
catch (Exception e) {
sleep(3000L);
}
}
return null;
}
至此,我們已經獲取到了頁面的html,接下來,是解析數據。不過要注意,微博的頁面數據是用FM.view()填充的,所以需要進行處理。下面,是我以前寫的解析例子,至今還是可以用的
/**
* @Title: parseData
* @Description: TODO(解析頁面數據轉化爲集合)
* @param html
* @param domain
* @return
* List<WeiboDomainGroup> 返回類型
*/
public List<WeiboDomain> parseData(String html){
List<WeiboDomain> result = new ArrayList<>();
Document doc = Jsoup.parse(html);
//處理填充數據
String str = "";
Elements scripts = doc.getElementsByTag("script");
//找到包含數據的script
for (int i=0;i<scripts.size();i++) {
String script = scripts.get(i).html();
if (script.contains("pl.content.signInPeople.index")) {
str = getHtml(script);
break;
}
}
//解析頁面數據
doc = Jsoup.parse(str);
Elements user = doc.getElementsByTag("dd");
for (Element element : user){
if (element.attr("class").equals("mod_info S_line1")) {
WeiboDomain weiboDomainGroup = new WeiboDomain();
String uid = "";
Elements elements = element.getElementsByTag("div");
for (Element div : elements){
if (div.attr("class").equals("info_name W_fb W_f14")){
Element S_txt1 = div.getElementsByClass("S_txt1").get(0);
uid = S_txt1.attr("usercard").split("&")[0].replaceAll("id=", "");
weiboDomainGroup.setUid(uid);
weiboDomainGroup.setUrl(S_txt1.attr("href"));
weiboDomainGroup.setName(S_txt1.attr("title"));
Elements i = div.getElementsByTag("i");
for (Element ele : i){
if (ele.attr("class").equals("W_icon icon_member")){
weiboDomainGroup.setVip(true);
}
if (ele.attr("class").equals("W_icon icon_male")){
weiboDomainGroup.setGender("m");
}
else{
weiboDomainGroup.setGender("f");
}
}
}
if (div.attr("class").equals("info_connect")) {
Elements em = div.getElementsByTag("em");
weiboDomainGroup.setFriends_count(
Integer.parseInt(em.get(0).text()));
weiboDomainGroup.setFollowers_count(em.get(1).text());
weiboDomainGroup.setStatuses_count(
Integer.parseInt(em.get(2).text()));
}
if (div.attr("class").equals("info_add")){
Elements span = div.getElementsByTag("span");
weiboDomainGroup.setLocation(span.get(0).text());
}
if (div.attr("class").equals("info_intro")){
Elements span = div.getElementsByTag("span");
weiboDomainGroup.setDescription(span.get(0).text());
}
if (div.attr("class").equals("info_relation")){
String tag = div.text().split(":")[1];
weiboDomainGroup.setTag(tag);
}
}
weiboDomainGroup.setUpdateTime(LocalDateTime.now()
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
result.add(weiboDomainGroup);
}
}
return result;
}
/**
* @Title: getHtml
* @Description: TODO(微博數據是用FW.view填充,所以需要解析)
* @return
* String 返回類型
*/
private String getHtml(String str) {
str = str.replaceAll("FM.view\\(", "").replaceAll("\\)", "");
JSONObject json = JSONObject.fromObject(str);
return json.getString("html");
}
實體類
/**
* @ClassName: entity
* @Description: TODO(這裏用一句話描述這個類的作用)
* @date 2018年2月23日 下午2:37:27
*/
@Data
public class WeiboDomain {
private String uid;
private String name;
private String url;
private String gender;
private String location;
private String description;
private String tag;
private String followers_count;
private Integer friends_count;
private Integer statuses_count;
private boolean isVip;
private String updateTime;
}
最後,我們稍微測試下,準確無誤。 over
四、注意事項
1、截止2018年7月31日 23:02:05。 代碼可用
2、該方法適用於小規模需求,比如爬某幾個賬號的微博內容、點贊、轉發數。且無法實現自動化
3、cookie具有時效性,過期時間好像是12個小時?
4、cookie中帶有賬號信息,爬取太多次可能會被封賬號
五、模擬登錄
以上的方法是不可以實現自動化,但是我們,可以通過模擬登錄獲取cookie,實現自動化。本來是這樣,不過,博主今天測試的時候發現自己以前寫的模擬登錄,已經不可用了。博主後面會講怎麼用另一種方法自動獲取cookie。所以,這方面大家自己找資料吧,博主也就不修復了
六、代碼
代碼已經上傳到csdn(https://download.csdn.net/download/qq_41057280/10575744),需要可自行下載。如果沒有積分的話,可以聯繫博主,聯繫方式開頭寫了