从人人网抓取高校数据信息,包括,省份 - 高校 - 院系 (提供最终SQL文件下载)...
從人人網抓取高校數據信息,包括,省份 - 高校 - 院系?
Author: handt
Blog ? ?: www.cnblogs.com/handt
涉及到的腳本和 SQL 文件 點此下載
=========== 正文部分 ==============
思路分析:
1. 獲得高校數據
地址:http://s.xnimg.cn/a44177/allunivlist.js (通過分析頁面請求確定為該文件)
對該文件進行人工分析,結合頁面彈出框,可以得知,該js文件包含了 國家、城市、學校 信息。文件為 “非標準” json格式:所有key值均未加雙引號。比如其中大學信息格式為:
{id:1,univs:[{id:1001,name:"\u6e05\u534e\u5927\u5b66"}
標準 JSON 格式的id,univs,name 都應該用雙引號包圍。關于JSON格式的簡單介紹可以參見《PHP 與 JSON》
?
因此,第一步 應該是格式化數據文件,轉換為標準JSON格式,以便直接用PHP的 json_decode 函數處理。
接下來,具體分析該文件的字段信息。
出于提高訪問速度的考慮,allunivlist.js 文件被壓縮在同一行。為了快速分析該文件,提供一個小技巧:將文件另存到本地,用vim打開,使用 vim 的括號匹配功能(shift+5)從最外層逐漸開始分析。(vim更多知識可以參見《Vimtutor拾遺》)
經過分析,文件整體結構為
[{國家1},{國家2},{國家3}....]
國家定義:{id:xxx, univs:xxxx, name:xxxx, provs:xxxx, country_id:xxx} ?(provs表示省份)
provs:[{省份1},{省份2},{省份3}....]
省份定義:{id:xxx, univs:xxx, country_id:xxxx, name:xxxx} (univs表示大學)
univs: [{大學1},{大學2},{大學3}....]
大學定義:{id:xxx, name:xxxx}
通過該文件,能夠獲取到中國所有省份的高校信息。
對應的,可以建立數據表school:
1 CREATE TABLE `school` ( 2 `id` int(11) NOT NULL AUTO_INCREMENT, 3 `sid` int(11) DEFAULT NULL COMMENT '學校id', 4 `cid` int(11) DEFAULT NULL COMMENT '所在城市id', 5 `name` varchar(255) CHARACTER SET utf8 DEFAULT NULL COMMENT '名字', 6 PRIMARY KEY (`id`) 7 ) ENGINE=InnoDB AUTO_INCREMENT=3198 DEFAULT CHARSET=latin1;?
2. 獲取各高校的院系信息
人人網查詢高校院系的接口為:http://www.renren.com/GetDep.do?id=高校ID
這里的 高校ID 即為1中“大學定義”部分高亮標識的id,亦即數據表中的sid。
該接口的返回信息很簡單,一個<select>元素,其中的<option> value 就是院系信息。
對于院系信息,可以建立數據表 department:
1 CREATE TABLE `department` ( 2 `id` int(11) NOT NULL AUTO_INCREMENT, 3 `sid` int(11) NOT NULL COMMENT 'school id', 4 `did` int(11) NOT NULL COMMENT 'department id', 5 `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT 'department 名字', 6 PRIMARY KEY (`id`) 7 ) ENGINE=InnoDB AUTO_INCREMENT=30044 DEFAULT CHARSET=latin1;確立了思路之后,下面開始動手。采用 PHP + MYSQL 完成編碼部分內容。
1. 獲得高校數據:
1.1 讀取文件并將初始化為標準 JSON 格式
將?http://s.xnimg.cn/a44177/allunivlist.js 文件另存到本地。
因為這里只需要中國的數據,可以將?[{國家1},{國家2},{國家3}....] 中其他國家的內容刪除。同時,開頭的 ‘?var allUnivList=’ 也刪掉。簡單處理后,文件在括號匹配上是符合json格式的。
接下來讀取文件,對形如 ‘#(?P<name>\w+):#i’ 的數據,進行替換,在前后均添加雙引號。
讀取文件和初始化代碼:
1 function x_readfile($filename) 2 { 3 $handle = fopen($filename, 'r'); 4 $contents = fread($handle, filesize($filename)); 5 fclose($handle); 6 return $contents; 7 } 8 9 $content = x_readfile('allunivlist.js'); 10 // 將文件初始化為標準JSON格式 11 $pat = '#(?P<name>\w+):#i'; 12 $replacement = '"${1}":'; 13 $str = preg_replace($pat, $replacement, $content); 14 $ret = json_decode($str, true); // true 返回的是關聯array類型,否則為class?
1.2 生成要插入數據庫的語句:
通過循環,生成形如 ‘insert into school(sid, cid, name) values(xxx, xxx, xxx)’的語句。
$ret 變量是 json_decode 后的關聯數組,回顧 1 中分析的數據結構:
provs:[{省份1},{省份2},{省份3}....]
省份定義:{id:xxx,?univs:xxx, country_id:xxxx, name:xxxx} univs表示大學
univs: [{大學1},{大學2},{大學3}....]
大學定義:{id:xxx, name:xxxx}·
利用如下代碼進行處理:
1 // 提取 城市 - 學校 信息 2 // 省份{id, univs, country_id, name} 3 // 學校{id, name} 4 foreach($ret as $k => $v) 5 { 6 // $k : order id 7 // $v : detail array(id, univs, conntry_id, name) 8 // table: id, cid, sid, name (city, school, name) 9 $name = my_decode($v['name']); 10 create_insert_sql($v['id'], 0, $name); // $v['univs']表示當前省份下的所有高校 11 foreach($v['univs'] as $university){ 12 $name = my_decode($university['name']); // 學校{id, name} 13 create_insert_sql($university['id'], $v['id'], $name); 14 } 15 }這里用到了my_decode函數,對形如‘\uaaaa\ubbbb’的字符進行編碼處理:
1 function replace_unicode_escape_sequence($match) { 2 return mb_convert_encoding(pack('H*', $match[1]), 'UTF-8', 'UTF-8'); 3 } 4 // solution from:http://stackoverflow.com/questions/2934563/how-to-decode-unicode-escape-sequences-like-u00ed-to-proper-utf-8-encoded-cha 5 function my_decode($str) 6 { 7 return $str = preg_replace_callback('/\\\\u([0-9a-f]{4})/i', 'replace_unicode_escape_sequence', $str); 8 }create_insert_sql 函數用來生成進行插入的sql 語句。為了方便,這里直接將sql語句用echo顯示在網頁上。再將網頁文字直接復制到sql 客戶端中(筆者使用的navicat)執行。
1 function create_insert_sql($sid, $cid, $name) 2 { 3 $sql = 'insert into ' . TABNAME . '(sid, cid, name) values (' . $sid . ',' . $cid . ',"' . $name . '");'; 4 echo $sql . '<br/>'; 5 return $sql; 6 }獲取高校信息的完整代碼如下(點擊展開):
View Code <?php define('TABNAME', 'school');function x_readfile($filename) {$handle = fopen($filename, 'r');$contents = fread($handle, filesize($filename));fclose($handle);return $contents; } function replace_unicode_escape_sequence($match) {return mb_convert_encoding(pack('H*', $match[1]), 'UTF-8', 'UTF-8'); } // solution from:http://stackoverflow.com/questions/2934563/how-to-decode-unicode-escape-sequences-like-u00ed-to-proper-utf-8-encoded-cha function my_decode($str) {return $str = preg_replace_callback('/\\\\u([0-9a-f]{4})/i', 'replace_unicode_escape_sequence', $str); }function create_insert_sql($sid, $cid, $name) {$sql = 'insert into ' . TABNAME . '(sid, cid, name) values (' . $sid . ',' . $cid . ',"' . $name . '");';echo $sql . '<br/>';return $sql; }$content = x_readfile('cu.js'); // 將文件初始化為標準JSON格式 $pat = '#(?P<name>\w+):#i'; $replacement = '"${1}":'; $str = preg_replace($pat, $replacement, $content); $ret = json_decode($str, true); // 提取 城市 - 學校 信息 // 省份{id, univs, country_id, name} // 學校{id, name} foreach($ret as $k => $v) {// $k : order id// $v : detail array(id, univs, conntry_id, name)// table: id, cid, sid, name (city, school, name)$name = my_decode($v['name']);create_insert_sql($v['id'], 0, $name);foreach($v['univs'] as $university){$name = my_decode($university['name']);create_insert_sql($university['id'], $v['id'], $name);} }?>?
2. 獲得院系信息
在 1 中已經成功生成了 省份 - 高校 數據。院系信息只需要通過接口,循環訪問即可。
2.1 獲取學校ID 集合
標準的數據庫連接操作。利用while循環,將所有sid存儲在 $sids 數組中。
1 // 獲取所有學校id 2 $link = mysql_connect(MYSQL_HOST, MYSQL_USER, MYSQL_PASS); 3 if (!$link) { 4 die('Could not connect: ' . mysql_error()); 5 } 6 $db_selected = mysql_select_db(DATABASE, $link); 7 if (!$db_selected) { 8 die ('Can\'t use : ' . mysql_error()); 9 } 10 mysql_query("set names utf8;"); 11 $sql_statement = 'select sid from school where cid != 0'; 12 $result = mysql_query($sql_statement, $link); 13 $sids = array(); 14 while($row = mysql_fetch_array($result)){ 15 $sids[] = $row['sid']; 16 }2.2 調用接口,循環訪問各高校院系數據
這里使用到了存儲過程進行數據庫讀寫操作,具體分析參看下文的 A) 部分。
1 // 根據學校 id 去發起請求,一共3163個學校,可見請求之多 2 $baseurl = 'http://www.renren.com/GetDep.do?id='; 3 for($i = 0; $i < count($sids); $i++){ 4 $content = x_readfile($baseurl . $sids[$i]); 5 $pat = '#<option[^>].*?>(.*?)</option>#i'; 6 $matches = array(); 7 preg_match_all($pat, $content, $matches); 8 // 拋棄第一條結果:<option value=''>院系</option> 9 array_shift($matches[1]); 10 try { 11 $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); 12 13 $dbh->beginTransaction(); 14 $dbh->exec('set names utf8;'); 15 foreach($matches[1] as $k => $v){ 16 $dbh->exec(create_insert_sql($sids[$i], $k + 1, mb_convert_encoding($v, 'UTF-8', 'HTML-ENTITIES'))); 17 } 18 $dbh->commit(); 19 20 } catch (Exception $e) { 21 $dbh->rollBack(); 22 echo "Failed: " . $e->getMessage(); 23 } 24 }因為是訪問網絡文件,x_readfile有所改變:
1 function x_readfile($filename) 2 { 3 $handle = fopen($filename, 'r'); 4 $contents = stream_get_contents($handle); 5 return $contents; 6 }?
沒有過于復雜的思路,不過細節上要提到兩點。
A) 使用存儲過程寫入數據庫
在1.2中生成的sql語句,是直接使用mysql客戶端執行的。對于3000多條數據,一條條執行,速度很慢。考慮到院系數據可能是高校數據的10倍,應該用更快的方式寫數據庫。因此,使用到了存儲過程。
PHP中存儲過程的使用可以參看PHP手冊,這里給出示范代碼:
1 // 初始化 PDO 存儲過程 2 try { 3 $dsn = 'mysql:dbname=' . DATABASE . ';host=127.0.0.1'; 4 $dbh = new PDO($dsn, MYSQL_USER, MYSQL_PASS, 5 array(PDO::ATTR_PERSISTENT => true)); 6 } catch (Exception $e) { 7 die("Unable to connect: " . $e->getMessage()); 8 } 9 ....... 10 ....... 11 try { 12 $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); 13 14 $dbh->beginTransaction(); 15 $dbh->exec('set names utf8;'); 16 foreach($matches[1] as $k => $v){ 17 $dbh->exec(create_insert_sql($sids[$i], $k + 1, mb_convert_encoding($v, 'UTF-8', 'HTML-ENTITIES'))); 18 } 19 $dbh->commit(); 20 21 } catch (Exception $e) { 22 $dbh->rollBack(); 23 echo "Failed: " . $e->getMessage(); 24 }B) 數據庫亂碼
因為院系信息返回的格式形如”&#xxxxx;&#xxxxx“,進行了十進制編碼,需要進行轉換。
mb_convert_encoding($v, 'UTF-8', 'HTML-ENTITIES')
在插入數據庫的時候,發現 department.name字段 全部是亂碼,而數據表的format中已將name字段設置為 UTF-8 字符集。經驗分析,是在寫入數據庫時的編碼出了問題,因此,在每次寫數據庫前加一條語句:
$dbh->exec('set names utf8;');
這樣就沒有亂碼了。
獲取院系信息的完整代碼如下(點擊展開):
View Code 1 <?php 2 define('DATABASE', 'test'); 3 define('TABNAME', 'department'); 4 define('MYSQL_HOST', 'localhost'); 5 define('MYSQL_USER', 'root'); 6 define('MYSQL_PASS', 'root'); 7 8 function x_readfile($filename) 9 { 10 $handle = fopen($filename, 'r'); 11 $contents = stream_get_contents($handle); 12 return $contents; 13 } 14 15 function create_insert_sql($sid, $did, $name) 16 { 17 $sql = 'insert into ' . TABNAME . '(sid, did, name) values (' . $sid . ',' . $did . ',"' . $name . '");'; 18 //echo $sql . '<br/>'; 19 return $sql; 20 } 21 // 獲取所有學校id 22 $link = mysql_connect(MYSQL_HOST, MYSQL_USER, MYSQL_PASS); 23 if (!$link) { 24 die('Could not connect: ' . mysql_error()); 25 } 26 $db_selected = mysql_select_db(DATABASE, $link); 27 if (!$db_selected) { 28 die ('Can\'t use : ' . mysql_error()); 29 } 30 mysql_query("set names utf8;"); 31 $sql_statement = 'select sid from school where cid != 0'; 32 $result = mysql_query($sql_statement, $link); 33 $sids = array(); 34 while($row = mysql_fetch_array($result)){ 35 $sids[] = $row['sid']; 36 } 37 // 初始化 PDO 存儲過程 38 try { 39 $dsn = 'mysql:dbname=' . DATABASE . ';host=127.0.0.1'; 40 $dbh = new PDO($dsn, MYSQL_USER, MYSQL_PASS, 41 array(PDO::ATTR_PERSISTENT => true)); 42 } catch (Exception $e) { 43 die("Unable to connect: " . $e->getMessage()); 44 } 45 // 根據學校 id 去發起請求,一共3163個學校,可見請求之多 46 $baseurl = 'http://www.renren.com/GetDep.do?id='; 47 for($i = 0; $i < count($sids); $i++){ 48 $content = x_readfile($baseurl . $sids[$i]); 49 $pat = '#<option[^>].*?>(.*?)</option>#i'; 50 $matches = array(); 51 preg_match_all($pat, $content, $matches); 52 // 拋棄第一條結果:<option value=''>院系</option> 53 array_shift($matches[1]); 54 try { 55 $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); 56 57 $dbh->beginTransaction(); 58 $dbh->exec('set names utf8;'); 59 foreach($matches[1] as $k => $v){ 60 $dbh->exec(create_insert_sql($sids[$i], $k + 1, mb_convert_encoding($v, 'UTF-8', 'HTML-ENTITIES'))); 61 } 62 $dbh->commit(); 63 64 } catch (Exception $e) { 65 $dbh->rollBack(); 66 echo "Failed: " . $e->getMessage(); 67 } 68 } 69 ?>?
最后附上兩張最終SQL文件結果圖:
?
?
一個直接使用這些數據的例子:
《制作一個選擇中國大學的彈框 (數據、步驟、代碼)》
http://www.cnblogs.com/technology/archive/2012/07/30/2607560.html
?
轉載于:https://www.cnblogs.com/handt/archive/2013/03/11/2954511.html
總結
以上是生活随笔為你收集整理的从人人网抓取高校数据信息,包括,省份 - 高校 - 院系 (提供最终SQL文件下载)...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: pgsql动态创建table
- 下一篇: WCF端口共享