上週寫了一個騰訊旗下的一個小說網站的自動回帖程序:javascript
具體怎麼實現的呢? html
其實它就是一個,找到評論接口,而後利用程序模擬HTTP請求的過程。再結合爬蟲的相關技術具體實現。 大概分爲這麼幾步:java
第一步:先找到評論接口:sql
使用chrome或者火狐瀏覽器,或者專業點的fiddler對評論過程抓包chrome
獲得具體的請求爲:數據庫
POST http://chuangshi.qq.com/bookcomment/replychapterv2 HTTP/1.1
Host: chuangshi.qq.com Connection: keep-alive Content-Length: 102 Accept: application/json, text/javascript, */*; q=0.01 Origin: http://chuangshi.qq.com X-Requested-With: XMLHttpRequest User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.93 Safari/537.36 Content-Type: application/x-www-form-urlencoded; charset=UTF-8 Referer: http://chuangshi.qq.com/bk/xh/AGkEMF1hVjEAOlRlATYBZg-r-69.html Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.8 Cookie: xxxxxx bid=14160172&uuid=69&content=%E4%B9%A6%E5%86%99%E7%9A%84%E4%B8%8D%E9%94%99&_token=czo4OiJJRm9TQ0RnbSI7
第二步:模擬請求,它的邏輯是:首先經過頻道頁(玄幻·奇幻 武俠·仙俠 都市·職場 歷史·軍事 遊戲·體育 科幻·靈異 二次元)抓取文章信息。json
如玄幻小說的頻道頁爲:http://chuangshi.qq.com/bk/huan/ 瀏覽器
而後提取文章標題、文章ID,章節ID等數據服務器
注入cookie等參數,模擬請求核心代碼:cookie
1 public class AutoCommentService : AutoComment.service.IAutoCommentService 2 { 3 public delegate void CommentHandler(Article article,Comment comment); 4 public event CommentHandler Commented; 5 bool stop; 6 /// <summary>
7 /// 是否中止服務 8 /// </summary>
9 public bool Stop 10 { 11 get { return stop; } 12 set { stop = value; } 13 } 14
15 /// <summary>
16 /// 初始化數據庫 17 /// </summary>
18 public void InitDatabase() 19 { 20 SQLiteConnection.CreateFile(AppSetting.DB_FILE);//建立SQL文件
21 SQLiteConnection con = new SQLiteConnection();//創建鏈接
22 SQLiteConnectionStringBuilder sqlstr = new SQLiteConnectionStringBuilder();//構建鏈接字符串
23 sqlstr.DataSource = AppSetting.DB_FILE; 24 con.ConnectionString = sqlstr.ToString(); 25 con.Open();//打開鏈接
26 string cmdStr = @"create table comment ( 27 id integer primary key autoincrement, 28 content varchar(500), 29 articleId varchar(50), 30 articleName varchar(50), 31 url varchar(50) 32 )"; 33 SQLiteCommand cmd = new SQLiteCommand(cmdStr, con); 34 cmd.ExecuteNonQuery(); 35 con.Close();//關閉鏈接
36 } 37
38 /// <summary>
39 /// 運行自動評論 40 /// </summary>
41 public void RunAutoComment() 42 { 43 this.Stop = false; 44 List<string> articleTypes = AppSetting.articleType; 45 while (!Stop) 46 { 47 foreach (var type in articleTypes) 48 { 49 string url = AppSetting.WEB_HOME_URL + "/bk/" + type; 50 var list = GetArticles(url); 51 foreach (var article in list) 52 { 53 AddComment(article, new Comment(AppSetting.commentContent)); 54 System.Threading.Thread.Sleep(AppSetting.waitSecond * 1000); 55 if (Stop) 56 break; 57 } 58 if (Stop) 59 break; 60 } 61 } 62 } 63
64 public void StopAutoComment() 65 { 66 this.Stop = true; 67 } 68
69 /// <summary>
70 /// 獲取頻道頁下全部最新發布的文章 71 /// </summary>
72 /// <param name="typeUrl">玄幻,軍事等頻道頁URL</param>
73 /// <returns></returns>
74 public List<Article> GetArticles(string typeUrl) 75 { 76 var html = HttpHelper.GetString(typeUrl); 77 html = Regex.Match(html, "<div class=\"update_list\">.*?(?=</div>)</div>").Value; 78 var arcticleUrlMatch = "<a class='gray2' title='(?<title>[^']*)' href='(?<url>[^']*)'>"; 79 MatchCollection matches = Regex.Matches(html, arcticleUrlMatch); 80 List<Article> articles = new List<Article>(); 81 if (matches != null) 82 { 83 foreach (Match match in matches) 84 { 85 if (match != null) 86 { 87 string url = match.Groups["url"].Value; 88 string subTitle = match.Groups["title"].Value; 89 string title = match.Groups["title"].Value; 90 if (string.IsNullOrEmpty(url) == false) 91 { 92 Article article = new Article(title,subTitle,url); 93 articles.Add(article); 94 } 95 } 96 } 97 } 98 return articles; 99 } 100
101 /// <summary>
102 /// 提交評論 103 /// </summary>
104 /// <param name="article"></param>
105 /// <param name="comment"></param>
106 /// <returns></returns>
107 public bool AddComment(Article article, Comment comment) 108 { 109 bool successed = false; 110 var html = HttpHelper.GetString(article.Url); 111 article.BID = Regex.Match(html, "var bid = \"(?<bid>[\\d]+)\"").Groups["bid"].Value; 112 article.UUID = Regex.Match(html, "uuid = \"(?<uuid>[\\d]+)\";").Groups["uuid"].Value; 113 article.Title = Regex.Match(html, "<title>(?<title>[^<]+)</title>").Groups["title"].Value.Replace("_創世中文", ""); 114 dal.CommentDal dal = new dal.CommentDal(); 115 if (dal.CanComment(article)) 116 { 117 HttpRequestParam param = new HttpRequestParam(); 118 string url = "http://chuangshi.qq.com/bookcomment/replychapterv2"; 119 param.Cookie = AppSetting.Cookie; 120 param.Method = "post"; 121 string token = AppSetting.Token; 122 string body = string.Format("bid={0}&uuid={1}&content={2}&_token={3}", article.BID, article.UUID, HttpHelper.UrlEncode(comment.Content), token); 123 param.Body = body; 124 var result = HttpHelper.GetHtml(url, param); 125 comment.Result = result.UnicodeToChinese(); 126 successed = result.Contains("status\":true"); 127 if (successed) 128 { 129 comment.Successed = 1; 130 dal.AddComment(comment, article); 131 } 132 else
133 { 134 comment.Successed = 0; 135 } 136
137 if (Commented != null) 138 { 139 Commented(article, comment); 140 } 141 } 142 return successed; 143 } 144
145 }
其餘的刷贊、和刷收藏方法都相似,都是找到接口,而後再模擬請求。
但大部分網站的這種相似登陸,評論,刷贊等接口都是有必定的反機器人策略的。
好比常見的策略:驗證碼、IP限制、帳號限制、接口調用頻次限制。
但作數據挖掘,作爬蟲和這種自動刷評論和系統方老是不斷博弈。一方攻,一方守,也都有應對的策略,那就簡單談下攻守方式吧。
1、驗證碼:
驗證碼(CAPTCHA)是「Completely Automated Public Turing test to tell Computers and Humans Apart」(全自動區分計算機和人類的圖靈測試)的縮寫,是一種區分用戶是計算機仍是人的公共全自動程序
防止惡意破解密碼、刷票、論壇灌水、刷頁。
固然,種類繁多啊
簡單驗證碼自動識別:
灰化》二值化》去噪》分割 》字符識別
第一步:灰化
就是把圖片變成黑白色
第二步:二值化
經過指定篩選的閾值,將特定灰度的像素點轉化成黑白兩色。
中值二值化:中值127爲閾值
均值二值化 :圖片全部像素的平均值
直方圖二值化:使用直方圖方法來尋找二值化閾值
維納濾波二值化:最小均方偏差
第三步:去噪
刪除掉干擾的冗餘像素點,即爲去噪
第四步:分割
將驗證碼根據邊界切割成單個字符
第五步:字符識別
根據匹配字符庫中的樣本,取類似度最高的字符來實現識別。
相關開源的ORC字符識別模塊可參考google開源的
2、IP限制
這個的話,使用多臺服務器、或者使用IP代理解決。
3、帳號限制
那就多個帳號唄,萬能的淘寶,啥都有賣。兩元一個的QQ號,幾十塊錢一大把的郵箱都是有的。有了帳號後再使用代碼實現自動登陸,自動切換帳號。
其實自動切換帳號,這個挺折騰的,尤爲是大公司的網站。腳本寫的很是複雜,你要想實現自動登陸,你就必須先得看懂它的代碼,提取它的登陸邏輯就行封裝。好比騰訊的WEB自動登陸密碼加密的方法:
它會hash,MD5,位移,編碼來來回回加密十來次,封裝起來仍是有點費勁的。
function getEncryption(password, salt, vcode, isMd5) {
vcode = vcode || '';
password = password || '';
var md5Pwd = isMd5 ? password : md5(password),
h1 = hexchar2bin(md5Pwd),
s2 = md5(h1 + salt),
rsaH1 = $pt.RSA.rsa_encrypt(h1),
rsaH1Len = (rsaH1.length / 2).toString(16),
hexVcode = TEA.strToBytes(vcode.toUpperCase(), true),
vcodeLen = Number(hexVcode.length / 2).toString(16);
while (vcodeLen.length < 4) {
vcodeLen = '0' + vcodeLen
}
while (rsaH1Len.length < 4) {
rsaH1Len = '0' + rsaH1Len
}
TEA.initkey(s2);
var saltPwd = TEA.enAsBase64(rsaH1Len + rsaH1 + TEA.strToBytes(salt) + vcodeLen + hexVcode);
TEA.initkey('');
setTimeout(function () {
__monitor(488358, 1)
}, 0);
return saltPwd.replace(/[\/\+=]/g, function (a) {
return {
'/': '-',
'+': '*',
'=': '_'
}
[
a
]
})
}
4、接口次數限制
下降接口調用速度再配合多帳號能夠解決問題。
那麼本身的網站怎麼防止爬蟲和惡意接口調用呢?
驗證碼:使用更復雜的驗證碼,字母重疊的,連到一塊的。
IP限制:這個算了,限制沒啥用。
帳號限制:一旦監測到機器人行爲,根據狀況封號,先封1小時、還敢調用封它一天,還敢?那永久封號,這招很狠的。
核心接口使用HTTPS協議
固然我遇到過的最牛逼的反爬系統則是阿里的釘釘了,聽說是採用阿里最牛的加密技術作的。好奇的朋友能夠去抓下包看看,真的是啥線索都沒有。