alexzhan 发表于 2010-5-5 22:57

应版主之邀,晒出我的做法(华中A题)

本帖最后由 alexzhan 于 2010-5-6 12:33 编辑

4.30出的题目,5.1中午1点我才看到华中题目。原来不想做华中的,因为苏北都交了报名费了。后来在我们学校的一个群上看到讨论说华中A题是做数据挖掘的,我就去看了下题目,结果肠子都悔青了,我白白浪费一天多的时间。后来做,只有我一个人,说实话,这次比赛我完全在写爬虫,结果,杯具的是,我没有全部完成。因为后来还要写论文,论文也是草草完成了。(本来不想完成论文了,但是为了给自己一个交代,我还是硬着头皮交了上去,也算是对我三天的努力的肯定吧。)   这个A题做得比较好的,也来讨论下吗,或者把你做的贴出来。我也学习学习下。摘要:中国互联网经历了十年的快速发展期,已经形成较为成熟的应用。互联网论坛已经成为互联网企业与用户、用户与用户之间重要的互动平台。在这样的互动氛围中衍生出了很多商业机会与运营难题。解决这些运营难题的首要条件是,企业能够对论坛用户进行有效识别。这些识别要达到四个效果:言论领袖的确定,话题用户的定位,活跃用户的识别以及人际关系圈的发掘。本文通过独立编写能获取论坛数据的爬虫程序,把论坛网页中含有有用数据的部分源码下载到本地文件中,对这些源码分析并处理后写进关系型数据库,并建立针对论坛的数据挖掘通用模型对这些数据进行数据挖掘分别达到上述四个效果,从而实现对论坛用户的有效识别。

本文没有采取通用的爬虫算法,而是通过对论坛URL结构和网页内容显示框架进行分析后编写的对大多数论坛都通用的爬虫算法,这也为使得论坛数据的获取变得相对容易一些。
本文通过对用户所发帖子、精华帖子、加分帖子、别人回复的帖子的数量进行分析,对帖子数量利用极差分析法规范化后加权求用户得分,从而确定得分最高的用户就是言论领袖;通过对用户总在线时间和发帖数、回帖数利用极差分析法进行规范并加权求和后得到活跃度较高的用户。
对于话题用户的确定和关系圈的挖掘,本文利用向量空间模型,把用户所发帖子内容表示成文档向量形式,通过相似性计算对文本(帖子)聚类,并最终确定人际关系圈和话题用户。******************************************************************************数据获取方法(爬虫算法介绍)(图片我都没贴上,因为我做题是在我另一台电脑上,图片在那台电脑上,word直接复制不来图片)论坛数据的获取在本文要解决的问题中是一个很重要的问题。由于数据挖掘要求数据必须是结构化的,所以本文先把论坛上的数据通过一定的策略获取并转移到关系型数据库中。
本文采用java语言从论坛目标网页上实现数据获取,关系型数据库选用SqlServer数据库。
为便于描述,将数据库表结构罗列如下:Topic表: id1(主键) 、tid(帖子在论坛上的ID) 、url(帖子的入口URL)、 uid(发帖人在论坛上的ID) 、jh(帖子是否精华)、jf(帖子是否被加分)Commentb表:id2(主键) 、tid(帖子在论坛上的ID) 、uid(发帖人在论坛上的ID)、suid(回帖人在论坛上的ID) 、title(帖子题目) 、ttext(帖子内容) 、stext(回帖内容)BbsUser表:id(主键) 、uid(发帖人在论坛上的ID) 、name(发帖人的昵称) 、ontm(用户在线时间) 、ft(发帖数) 、ht(回帖数) 、zhf(别人总回复数) 、zjh(总精华数) 、 zjf(总加分数)从以上数据库表结构中可以看出,发帖与跟帖是一对多的关系。现如今,多数论坛都采用开源框架,比如国内比较著名的discuz和phpwind,而discuz最受欢迎,但是由于论坛的原理都是一样的,所不同的只是编码的方式、外在的表现以及局部的细节,因此针对一个论坛所编写的爬虫程序经过些许改动就能在另外一个相似的论坛上面运行,考虑到discuz用户数众多,因此程序只针对discuz7.2版本开发,而题目中所提到的四个论坛有三个是用discuz,本文选择http://diybbs.it168.com开发爬虫程序。如果让爬虫程序把从网页上面读取到的字符流经过处理直接存进数据库,由于页面众多,这样的处理过程实际上严重增加了网络开销,程序要运行很长时间。比较好的做法是,把在网页上读到的字符流经过初步简单处理,也就是用正则表达式匹配包含要收集的数据源信息,然后将数据写入多个文本文件(可以采取一个论坛的版块一个文本文件的方式)(参见TextCr.java),这个时候不把数据存入数据库而是存入文本文件的原因是此时只是初步处理了网络上的字符流而还没有获取最终需要的数据,因此先写进文本文件,然后再写个程序(参见TopicDb.java)从这些文本文件读取数据存入数据库中,因为这时候程序没有网络开销,所以这时候进行复杂的处理也不会对程序运行时间产生很大的影响。
以上的过程也是分布进行的。具体的说,是先从论坛上所有版块往板块内部抓取,可以设置一个抓取深度,因为毕竟一个论坛上的数据量是很惊人的,我们可以选取最近一段时间的帖子来抓取。为方便,本文选取深度为5(也就是从论坛每个版块首页网版块内部抓取5个网页为止)。
对于http://diybbs.it168.com来说,就是http://diybbs.it168.com/forum/-i-j.html其中的i代表第i个版块,j代表页面深度,http://diybbs.it168.com一共有将近200个版块(i最大是212,但是零到212中间有的版块不存在),j在本文中取1,2,3,4,5从上图可以看到从版块页面(也就是还没进到帖子内部去看),可以看到帖子的标题、回复次数、是否精华、是否被加分、发帖人信息。这些信息恰好组成了Topic数据库表的字段名,因此可以先把论坛上的这些信息抓取下来,经过初步处理(正则表达式匹配),写进文本文件。文本文件截图为:其中文件名为版块在网页上的ID,一共到212,但是中间并不是全部都有,比如从上图可以看出6.txt不存在。文件内部结构如图:
从图中可以看出,文本文件内部还是包含了太多无用的信息,而要插进数据库只是很少一部分有用的数据。用java语言读取文本信息并提取出有用的信息存进数据库,具体过程可参见附录程序代码。现在还只是把帖子的标题、ID、URL、回复数、作者等信息存进了数据库,而帖子内容还没有获得。应该再从数据库中一次性读出所有帖子(取样也可)的Url,放进计算机内存,然后再次编写爬虫程序(参见CrawlComment.java)去读取帖子内容以及回复内容、回复人的信息等。这次爬虫的处理也还是先把网络上的字符流初步处理下存进文本文件,最终通过对文本文件的处理获取需要的数据存进数据库(参见OtherDb.java)。最终数据库Topic表信息如下图所示:
  ************************************************************************
模型里面公式不好弄,我也不贴了。给出代码吧,如果想学习,可以拿去学习下。(只是java代码OtherDb.java程序部分功能没实现,当时要急着写论文,就没去深究。这两天忙着别的事情,也没完成。如果你有兴趣,可以完成下。具体是写进表comment没实现) 附录1TextCr.java 用于抓取Topic数据库表代码package huazhong; import java.io.File;import java.io.FileWriter;import java.io.FileNotFoundException;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.net.MalformedURLException;import java.net.URL;import java.net.URLConnection;import java.util.Date;import java.util.regex.Matcher;import java.util.regex.Pattern; public class TextCr {
publicstatic void main(String[] args) {
File
dirFile;
dirFile= new File("E:\\HTopic");

if(!dirFile.exists()){
dirFile.mkdir();
}
Datestarttime=new Date();

inti,j;
for(i=1;i<213;i++){
FileWriterfw=null;
Stringtext="";
String
path=null;
for(j=1;j<11;j++){//每个版块挖掘深度为10
StringurlString="http://diybbs.it168.com/forum-"+i+"-"+j+".html";
StringBufferhtml_text = new StringBuffer();

try {


URLurl = new URL(urlString);

URLConnection conn =url.openConnection();

BufferedReader reader = newBufferedReader(new InputStreamReader(conn.getInputStream()));

String line = null;

while ((line = reader.readLine()) !=null){

html_text.append(line);//不分行,这样更加容易处理

}

reader.close();

}catch (FileNotFoundException e){

continue;

}
catch(MalformedURLException e) {
System.out.println("无效的URL: " + urlString);
}catch (IOException e) {
e.printStackTrace();
}

Patternpat = Pattern.compile("<th class=\"subject (.+?)<tdclass=\"lastpost\">");
Matchermat=pat.matcher(html_text);
if(!mat.find()){
continue;
}
while(mat.find()){
Stringstrr=mat.group();
System.out.println(strr);

text+=strr+"\n";
}

}
if(text.equals("")){
continue;
}
try{
path=dirFile+"\\"+i+".txt";
Filefile = new File(path);
if(!file.exists())
file.createNewFile();
fw=newFileWriter(file);
fw.write(text);
}catch(IOException e){
System.out.println("输入输出异常");

}finally{
if(fw!=null)
try{fw.close();}catch(IOException e){}}
}
Dateendtime=new Date();
longtime=endtime.getTime()-starttime.getTime();
System.out.println("用时:"+time+"ms");
}}   附录2 TopicDb.Java将上面程序得到的文本文件写进数据库package huazhong; import java.sql.Connection;import java.sql.DriverManager;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.Statement;import java.util.Date;import java.io.BufferedReader;import java.io.File;import java.io.FileReader;import java.io.IOException; public class TopicDb {
publicstatic void main(String[] args) {
Datestarttime=new Date();
inttidMark=1;
inti,hf,uid;
intpreuid,maxpage;
Stringurl=null;
StringmainUrl="http://diybbs.it168.com/";

Connection connection=null;
try{
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
connection= DriverManager.getConnection(
"jdbc:sqlserver://localhost:1433;DatabaseName=bbsdb",
"sa","123");
}catch(Exception e) {
System.out.println(e.getMessage());
}
Stringpath="E:/HTopic/";
Filef = new File(path);
File[]list = f.listFiles();
for(i=0;i<list.length;i++){
try{
FileReaderreader = new FileReader(list);
BufferedReaderbr = new BufferedReader(reader);
Stringstring = null;

while((string= br.readLine()) != null) {

intjh=0,jf=0;
if(string.indexOf("精华")!=-1){
jh=1;
}
if(string.indexOf("加分")!=-1){
jf=1;
}
if(string.indexOf("匿名")!=-1){
continue;
}
Stringstring2 ="<span id=\"thread_";
inta=string2.length();
intb=string.indexOf(string2);
intc=string.indexOf("\"",b+a);
Stringstring3 = string.substring(a+b, c);
preuid=Integer.parseInt(string3);
url=mainUrl + "thread-"+string3+"-1-1.html";
intd=string.lastIndexOf("</a></span>");
inte=string.lastIndexOf(">",d);
Stringstring4 =string.substring(e+1,d);
try{
maxpage= Integer.parseInt(string4);
}catch(NumberFormatException e2) {
maxpage=1;

}
intm=string.indexOf("<strong>");
intt="<strong>".length();
intn=string.lastIndexOf("</strong>");
Stringstring5 =string.substring(m+t,n);
try{
hf=Integer.parseInt(string5);
}catch(NumberFormatException e3) {
hf=0;
}

String string6="<ahref=\"space-uid-";
intx=string.indexOf(string6);
inty=string6.length();
intz=string.indexOf(".",x);
Stringstring7 =string.substring(x+y,z);
uid=Integer.parseInt(string7);
try{
Statementstmt=connection.createStatement();
ResultSetres = stmt.executeQuery("select tid from Topic where tid="+preuid);
if(res.next()){
continue;
}
PreparedStatementpstmt=null;
Stringexpr="insert into Topic (id1,tid,url,uid,jh,jf,hf,maxpage) values(?,?,?,?,?,?,?,?)";
pstmt=connection.prepareStatement(expr);
pstmt.setInt(1,tidMark);
pstmt.setInt(2,preuid);
pstmt.setString(3,url);
pstmt.setInt(4,uid);
pstmt.setInt(5,jh);
pstmt.setInt(6,jf);

pstmt.setInt(7,hf);
pstmt.setInt(8,maxpage);
pstmt.executeUpdate();
}catch(Exception e1) {
System.out.println(e1);

}
tidMark++;
}}catch(IOExceptione){
System.out.println("读取文件异常");
}
}

try{
connection.close();
}catch(Exception e) {
System.out.println(e);

}
Dateendtime=new Date();
longtime=endtime.getTime()-starttime.getTime();
System.out.println("用时:"+time+"ms");
}}  附录3CrawlComment.java 抓取回复的帖子到本地文件package huazhong; import java.io.BufferedReader;import java.io.File;import java.io.FileNotFoundException;import java.io.FileWriter;import java.io.IOException;import java.io.InputStreamReader;import java.net.MalformedURLException;import java.net.URL;import java.net.URLConnection;import java.sql.Connection;import java.sql.DriverManager;import java.sql.ResultSet;import java.sql.Statement;import java.util.Date;import java.util.regex.Matcher;import java.util.regex.Pattern; public class CrawlComment {
publicstatic void main(String[] args) {
Datestarttime=new Date();
File
dirFile;
StringurlMain="http://diybbs.it168.com/";
inttid,maxpage;
int[]tidArray=new int;
int[]maxArray=new int;
dirFile= new File("E:\\HComment");

if(!dirFile.exists()){
dirFile.mkdir();
}
Connectionconnection=null;
try{
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");

connection= DriverManager.getConnection(
"jdbc:sqlserver://localhost:1433;DatabaseName=bbsdb",
"sa","123");
Statementstatement=connection.createStatement();
Stringexpr="select id1,tid,maxpage from Topic where id1>20";/
ResultSetresultSet =statement.executeQuery(expr);
while(resultSet.next()){
intid1=resultSet.getInt("id1");
tid=resultSet.getInt("tid");
maxpage=resultSet.getInt("maxpage");
tidArray=tid;
maxArray=maxpage;
}try{
connection.close();
}catch (Exception e) {
System.out.println(e);

}
}catch(Exception e) {
System.out.println(e);

}
inti,j;
for(i=21;i<1001;i++){
Stringtitle="";
FileWriterfw=null;
Stringtext="";
String
path=null;
if(maxArray>2){
maxArray=2;
}
try{
StringurlString=urlMain+"/thread-"+tidArray+"-"+1+"-1.html";
StringBufferhtml_text1 = new StringBuffer();
try{
URLurl = new URL(urlString);
URLConnectionconn1 = url.openConnection();
BufferedReaderreader = new BufferedReader(new InputStreamReader(conn1.getInputStream()));
Stringline = null;

while ((line = reader.readLine()) !=null){


if(line.contains("h1")){


inta=line.lastIndexOf("<");



int b=line.lastIndexOf(">",a);


title=line.substring(b+1,a);


}



html_text1.append(line);

}

if(html_text1.indexOf("管理员")!=-1){//||html_text1.indexOf("版主")!=-1


continue;

}

reader.close();

Pattern pat =Pattern.compile("<divclass=\"popuserinfo\">(.+?)</td></tr></table>");

Matcher mat=pat.matcher(html_text1);



if(!mat.find()){


continue;

}



while(mat.find()){


Stringstrr=mat.group();


System.out.println(strr);


text+=strr+"\n";


}

}catch (FileNotFoundException e){


continue;


}

catch (MalformedURLException e) {


System.out.println("无效的URL: " + urlString);


}
}catch(Exception e) {
e.printStackTrace();
}
for(j=2;j<maxArray+1;j++){
StringurlString=urlMain+"/thread-"+tidArray+"-"+j+"-1.html";
StringBufferhtml_text = new StringBuffer();
try{


URLurl = new URL(urlString);

URLConnection conn =url.openConnection();


BufferedReader reader = newBufferedReader(new InputStreamReader(conn.getInputStream()));

String line = null;

while ((line = reader.readLine()) !=null){


html_text.append(line);

}

reader.close();


}catch (FileNotFoundException e){


continue;


}

catch (MalformedURLException e) {


System.out.println("无效的URL: " + urlString);


}catch(IOException e) {


e.printStackTrace();


}

Pattern pat =Pattern.compile("<divclass=\"popuserinfo\">(.+?)</td></tr></table>");

Matcher mat=pat.matcher(html_text);



if(!mat.find()){


continue;

}



while(mat.find()){


Stringstrr=mat.group();


System.out.println(strr);



text+=strr+"\n";


}
}
try{
path=dirFile+"\\"+i+".txt";
Filefile = new File(path);
if(!file.exists())
file.createNewFile();
fw=newFileWriter(file);
fw.write(title+"\n");
fw.write(text);
}catch(IOException e){
System.out.println("输入输出异常");

}finally{
if(fw!=null)
try{fw.close();}catch(IOException e){}
}
}
Dateendtime=new Date();
longtime=endtime.getTime()-starttime.getTime();
System.out.println("用时:"+time+"ms");
}
}   附录4OtherDb.java 将其余信息写进数据库package huazhong; import java.io.BufferedReader;import java.io.File;import java.io.FileReader;import java.io.IOException;import java.sql.Connection;import java.sql.DriverManager;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.Statement;import java.util.Date; public
class OtherDb {
public
static
void main(String[] args) {
Date starttime=new Date();
int idMark=1;
int id2Mark=1;
int i,uid,ontm,tid,suid;
String name=null,title=null;
//StringmainUrl="http://diybbs.it168.com/";
Connection connection=null;
try{
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
connection = DriverManager.getConnection(
"jdbc:sqlserver://localhost:1433;DatabaseName=bbsdb",
"sa", "123");
}catch (Exception e) {
System.out.println(e.getMessage());
}
String path="E:/HComment/";
File f = new File(path);
File[] list = f.listFiles();
for(i=0;i<list.length;i++){
int flag1=0,flag2=0;
try{
FileReader reader = new FileReader(list);
BufferedReader br = new BufferedReader(reader);
String string = null;

title=br.readLine();
string=br.readLine();
if(string.indexOf("<")==-1){
continue;
}
//

name=string.substring(string.indexOf("_blank\">")+8,string.indexOf("</a>"));

//

try{

uid=Integer.parseInt(string.substring(string.indexOf("<dd>")+4,string.indexOf(" ")));

}catch (Exception e) {


continue;


}

//

inta=string.lastIndexOf("小时");
int b=string.lastIndexOf(">",a);
try {
ontm=Integer.parseInt(string.substring(b+1,a).trim());
} catch (Exception e) {
continue;
}
int c=string.indexOf("ptid");
int d=string.indexOf("&",c);
try {
tid=Integer.parseInt(string.substring(c+5,d));
} catch (Exception e) {
continue;
}

try{
Statement stmt=connection.createStatement();
ResultSet res = stmt.executeQuery("select uid from BbsUser where uid="+uid);
if(res.next()){

flag1=1;
}
if(flag2==0){
PreparedStatement pstmt=null;
String expr="insertinto BbsUser (id,uid,name,ontm) values (?,?,?,?)";
pstmt =connection.prepareStatement(expr);
pstmt.setInt(1, idMark);
pstmt.setInt(2, uid);
pstmt.setString(3, name);
pstmt.setInt(4, ontm);
pstmt.executeUpdate();
idMark++;
}
}catch (Exception e1) {
System.out.println(e1);

}
while((string = br.readLine()) != null) {


suid=Integer.parseInt(string.substring(string.indexOf("<dd>")+4,string.indexOf(" ")));
name=string.substring(string.indexOf("_blank\">")+8,string.indexOf("</a>"));

//

//uid=Integer.parseInt(string.substring(string.indexOf("<dd>"+4),string.indexOf(" ")));

//

a=string.lastIndexOf("小时");
b=string.lastIndexOf(">",a);
ontm=Integer.parseInt(string.substring(b+1,a).trim());
c=string.indexOf("ptid");
d=string.indexOf("&",c);
tid=Integer.parseInt(string.substring(c+5,d));
try {
PreparedStatement pstmt =null;
String expr="insert into Comment(id2,tid,uid,suid) values (?,?,?,?)";
pstmt =connection.prepareStatement(expr);
pstmt.setInt(1, id2Mark);
pstmt.setInt(2, tid);
pstmt.setInt(3, uid);
pstmt.setInt(4, suid);
id2Mark++;
} catch (Exception e) {
System.out.println(e);
}
try{
Statement stmt=connection.createStatement();
ResultSet res = stmt.executeQuery("select uid from BbsUser where uid="+suid);
if(res.next()){
flag2=1;
}
if(flag2==0){
PreparedStatement pstmt=null;
String expr="insert into BbsUser (id,uid,name,ontm) values (?,?,?,?)";
pstmt =connection.prepareStatement(expr);
pstmt.setInt(1, id2Mark);
pstmt.setInt(2, suid);
pstmt.setString(3, name);
pstmt.setInt(4, ontm);
pstmt.executeUpdate();
idMark++;
}
}catch (Exception e1) {
System.out.println(e1);

}

}}catch(IOException e){
System.out.println("读取文件异常");
}
}

try{
connection.close();
}catch (Exception e) {
System.out.println(e);

}
Date endtime=new Date();
long time=endtime.getTime()-starttime.getTime();
System.out.println("用时:"+time+"ms");
}}

stq5267 发表于 2010-5-6 22:39

虽然我不懂java程序不懂爬虫理论,(我们组都是另外懂爬虫的同学找数据的)但我觉得你写的很有条理,应该是不错的文章,祝贺你……

r9691 发表于 2010-5-6 22:59

楼主好厉害哦.....
可惜我当时不懂这个,当时用的是网页抓取/信息提取/数据抽取软件工具包MetaSeeker V4.10.0,效果还算好.

starbinbin 发表于 2010-5-7 21:35

不得不承认,楼主爬虫写的很牛!
不过说到底这是数学建模而不是爬虫编写大赛,对吗?
如果只要爬虫写的好就够了,那华中赛的A题岂不是很没水平的一场比赛吗?
所以个人认为重点还是在于模型的建立:)

占YOU 发表于 2010-5-8 16:02

老兄,发点中文版的啊,!!!!!!!!!!!!!!!!!!!!!!!!!!…………………………………………………………

lovehaboy 发表于 2010-5-10 12:39

谢谢你!

1_ven 发表于 2010-5-12 14:05

仅从摘要看,写的不够成熟。。。。。。。。。。。。。。。。。。。。。。

smillpp 发表于 2010-5-12 21:53

数据挖掘牛人。~~~。。赞赏一个~

yulitingfeng 发表于 2010-6-5 22:54

有数据有屁用啊!

无语和老戴 发表于 2010-6-12 20:36

不错咯,不过我不懂
页: [1] 2
查看完整版本: 应版主之邀,晒出我的做法(华中A题)