爬虫的主要工做就是到搜狐的新闻首页上去抓取新闻,然后将新闻添加到数据库中。
代码其实很简单的:
LinkParser.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
|
import
com
.
sohu
.
SohuNews
;
import
java
.
util
.
HashSet
;
import
java
.
util
.
Set
;
import
org
.
htmlparser
.
Node
;
import
org
.
htmlparser
.
NodeFilter
;
import
org
.
htmlparser
.
Parser
;
import
org
.
htmlparser
.
filters
.
NodeClassFilter
;
import
org
.
htmlparser
.
filters
.
OrFilter
;
import
org
.
htmlparser
.
tags
.
LinkTag
;
import
org
.
htmlparser
.
util
.
NodeList
;
import
org
.
htmlparser
.
util
.
ParserException
;
/**
* 这个类是用来搜集新闻链接地址的。将符合正则表达式的URL添加到URL数组中。
* @author guanminglin
*/
public
class
LinkParser
{
// 获取一个网站上的链接,filter 用来过滤链接
public
static
Set
<String>
extracLinks
(
String
url
,
LinkFilter
filter
)
{
Set
<String>
links
=
new
HashSet
<String>
(
)
;
try
{
Parser
parser
=
new
Parser
(
url
)
;
parser
.
setEncoding
(
"gb2312"
)
;
// 过滤 <frame >标签的 filter,用来提取 frame 标签里的 src 属性所表示的链接
NodeFilter
frameFilter
=
new
NodeFilter
(
)
{
public
boolean
accept
(
Node
node
)
{
if
(
node
.
getText
(
)
.
startsWith
(
"frame src="
)
)
{
return
true
;
}
else
{
return
false
;
}
}
}
;
// OrFilter 来设置过滤 <a> 标签,和 <frame> 标签
OrFilter
linkFilter
=
new
OrFilter
(
new
NodeClassFilter
(
LinkTag
.
class
)
,
frameFilter
)
;
// 得到所有经过过滤的标签
NodeList
list
=
parser
.
extractAllNodesThatMatch
(
linkFilter
)
;
for
(
int
i
=
0
;
i
<
list
.
size
(
)
;
i
++
)
{
Node
tag
=
list
.
elementAt
(
i
)
;
if
(
tag
instanceof
LinkTag
)
// <a> 标签
{
LinkTag
link
=
(
LinkTag
)
tag
;
String
linkUrl
=
link
.
getLink
(
)
;
// url
if
(
filter
.
accept
(
linkUrl
)
)
{
links
.
add
(
linkUrl
)
;
}
}
else
// <frame> 标签
{
// 提取 frame 里 src 属性的链接如 <frame src="test.html"/>
String
frame
=
tag
.
getText
(
)
;
int
start
=
frame
.
indexOf
(
"src="
)
;
frame
=
frame
.
substring
(
start
)
;
int
end
=
frame
.
indexOf
(
" "
)
;
if
(
end
==
-
1
)
{
end
=
frame
.
indexOf
(
">"
)
;
}
String
frameUrl
=
frame
.
substring
(
5
,
end
-
1
)
;
if
(
filter
.
accept
(
frameUrl
)
)
{
links
.
add
(
frameUrl
)
;
}
}
}
}
catch
(
ParserException
e
)
{
e
.
printStackTrace
(
)
;
}
return
links
;
}
public
void
doParser
(
String
url
)
{
SohuNews
news
=
new
SohuNews
(
)
;
Set
<String>
links
=
LinkParser
.
extracLinks
(
url
,
new
LinkFilter
(
)
{
//提取以 http://news.sohu.com 开头的链接
public
boolean
accept
(
String
url
)
{
if
(
url
.
matches
(
"http://news.sohu.com/[\\d]+/n[\\d]+.shtml"
)
)
{
return
true
;
}
else
{
return
false
;
}
}
}
)
;
//循环迭代出连接,然后提取该连接中的新闻。
for
(
String
link
:
links
)
{
System
.
out
.
println
(
link
)
;
news
.
parser
(
link
)
;
//解析连接
}
}
//测试主页新闻,可以得到主页上所有符合要求的网页地址,并进行访问。
public
static
void
main
(
String
[
]
args
)
{
String
url
=
"http://news.sohu.com/"
;
LinkParser
parser
=
new
LinkParser
(
)
;
parser
.
doParser
(
url
)
;
}
}
|
上面这段带码比较简单,就是用来提取 http://news.sohu.com 上面的新闻连接 ,格式类似这样:http://news.sohu.com/20090518/n264012864.shtml
所以写了一小段的正则表达式来匹配他:
1
2
3
4
5
6
7
8
9
10
11
12
|
Set
<String>
links
=
LinkParser
.
extracLinks
(
url
,
new
LinkFilter
(
)
{
//提取以 http://news.sohu.com 开头的链接
public
boolean
accept
(
String
url
)
{
if
(
url
.
matches
(
"http://news.sohu.com/[\\d]+/n[\\d]+.shtml"
)
)
{
return
true
;
}
else
{
return
false
;
}
}
}
)
;
|
还有一个核心类就是用来解析搜狐新闻的类,该类用于重网页中提取出新闻,然后将新闻添加到数据库中。代码中还用到了一个NewsBean
这段代码就不贴出来了,很简单的POJO 代码。核心代码都在下面。
SohuNews.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
|
import
com
.
sohu
.
bean
.
NewsBean
;
import
com
.
sohu
.
db
.
ConnectionManager
;
import
java
.
util
.
ArrayList
;
import
java
.
util
.
List
;
import
java
.
util
.
logging
.
Level
;
import
java
.
util
.
logging
.
Logger
;
import
org
.
htmlparser
.
NodeFilter
;
import
org
.
htmlparser
.
Parser
;
import
org
.
htmlparser
.
beans
.
StringBean
;
import
org
.
htmlparser
.
filters
.
AndFilter
;
import
org
.
htmlparser
.
filters
.
HasAttributeFilter
;
import
org
.
htmlparser
.
filters
.
TagNameFilter
;
import
org
.
htmlparser
.
tags
.
Div
;
import
org
.
htmlparser
.
tags
.
HeadingTag
;
import
org
.
htmlparser
.
tags
.
Span
;
import
org
.
htmlparser
.
util
.
NodeList
;
import
org
.
htmlparser
.
util
.
ParserException
;
import
java
.
sql
.
PreparedStatement
;
import
java
.
sql
.
SQLException
;
/**
* 用于对搜狐网站上的新闻进行抓取
* @author guanminglin <[email protected]>
*/
public
class
SohuNews
{
private
Parser
parser
=
null
;
//用于分析网页的分析器。
private
List
newsList
=
new
ArrayList
(
)
;
//暂存新闻的List;
private
NewsBean
bean
=
new
NewsBean
(
)
;
private
ConnectionManager
manager
=
null
;
//数据库连接管理器。
private
PreparedStatement
pstmt
=
null
;
public
SohuNews
(
)
{
}
/**
* 获得一条完整的新闻。
* @param newsBean
* @return
*/
public
List
getNewsList
(
final
NewsBean
newsBean
)
{
List
list
=
new
ArrayList
(
)
;
String
newstitle
=
newsBean
.
getNewsTitle
(
)
;
String
newsauthor
=
newsBean
.
getNewsAuthor
(
)
;
String
newscontent
=
newsBean
.
getNewsContent
(
)
;
String
newsdate
=
newsBean
.
getNewsDate
(
)
;
list
.
add
(
newstitle
)
;
list
.
add
(
newsauthor
)
;
list
.
add
(
newscontent
)
;
list
.
add
(
newsdate
)
;
return
list
;
}
/**
* 设置新闻对象,让新闻对象里有新闻数据
* @param newsTitle 新闻标题
* @param newsauthor 新闻作者
* @param newsContent 新闻内容
* @param newsDate 新闻日期
* @param url 新闻链接
*/
public
void
setNews
(
String
newsTitle
,
String
newsauthor
,
String
newsContent
,
String
newsDate
,
String
url
)
{
bean
.
setNewsTitle
(
newsTitle
)
;
bean
.
setNewsAuthor
(
newsauthor
)
;
bean
.
setNewsContent
(
newsContent
)
;
bean
.
setNewsDate
(
newsDate
)
;
bean
.
setNewsURL
(
url
)
;
}
/**
* 该方法用于将新闻添加到数据库中。
*/
protected
void
newsToDataBase
(
)
{
//建立一个线程用来执行将新闻插入到数据库中。
Thread
thread
=
new
Thread
(
new
Runnable
(
)
{
public
void
run
(
)
{
boolean
sucess
=
saveToDB
(
bean
)
;
if
(
sucess
!=
false
)
{
System
.
out
.
println
(
"插入数据失败"
)
;
}
}
}
)
;
thread
.
start
(
)
;
}
/**
* 将新闻插入到数据库中
* @param bean
* @return
*/
public
boolean
saveToDB
(
NewsBean
bean
)
{
boolean
flag
=
true
;
String
sql
=
"insert into news(newstitle,newsauthor,newscontent,newsurl,newsdate) values(?,?,?,?,?)"
;
manager
=
new
ConnectionManager
(
)
;
String
titleLength
=
bean
.
getNewsTitle
(
)
;
if
(
titleLength
.
length
(
)
>
60
)
{
//标题太长的新闻不要。
return
flag
;
}
try
{
pstmt
=
manager
.
getConnection
(
)
.
prepareStatement
(
sql
)
;
pstmt
.
setString
(
1
,
bean
.
getNewsTitle
(
)
)
;
pstmt
.
setString
(
2
,
bean
.
getNewsAuthor
(
)
)
;
pstmt
.
setString
(
3
,
bean
.
getNewsContent
(
)
)
;
pstmt
.
setString
(
4
,
bean
.
getNewsURL
(
)
)
;
pstmt
.
setString
(
5
,
bean
.
getNewsDate
(
)
)
;
flag
=
pstmt
.
execute
(
)
;
}
catch
(
SQLException
ex
)
{
Logger
.
getLogger
(
SohuNews
.
class
.
getName
(
)
)
.
log
(
Level
.
SEVERE
,
null
,
ex
)
;
}
finally
{
try
{
pstmt
.
close
(
)
;
manager
.
close
(
)
;
}
catch
(
SQLException
ex
)
{
Logger
.
getLogger
(
SohuNews
.
class
.
getName
(
)
)
.
log
(
Level
.
SEVERE
,
null
,
ex
)
;
}
}
return
flag
;
}
/**
* 获得新闻的标题
* @param titleFilter
* @param parser
* @return
*/
private
String
getTitle
(
NodeFilter
titleFilter
,
Parser
parser
)
{
String
titleName
=
""
;
try
{
NodeList
titleNodeList
=
(
NodeList
)
parser
.
parse
(
titleFilter
)
;
for
(
int
i
=
0
;
i
<
titleNodeList
.
size
(
)
;
i
++
)
{
HeadingTag
title
=
(
HeadingTag
)
titleNodeList
.
elementAt
(
i
)
;
titleName
=
title
.
getStringText
(
)
;
}
}
catch
(
ParserException
ex
)
{
Logger
.
getLogger
(
SohuNews
.
class
.
getName
(
)
)
.
log
(
Level
.
SEVERE
,
null
,
ex
)
;
}
return
titleName
;
}
/**
* 获得新闻的责任编辑,也就是作者。
* @param newsauthorFilter
* @param parser
* @return
*/
private
String
getNewsAuthor
(
NodeFilter
newsauthorFilter
,
Parser
parser
)
{
String
newsAuthor
=
""
;
try
{
NodeList
authorList
=
(
NodeList
)
parser
.
parse
(
newsauthorFilter
)
;
for
(
int
i
=
0
;
i
<
authorList
.
size
(
)
;
i
++
)
{
Div
authorSpan
=
(
Div
)
authorList
.
elementAt
(
i
)
;
newsAuthor
=
authorSpan
.
getStringText
(
)
;
}
}
catch
(
ParserException
ex
)
{
Logger
.
getLogger
(
SohuNews
.
class
.
getName
(
)
)
.
log
(
Level
.
SEVERE
,
null
,
ex
)
;
}
return
newsAuthor
;
}
/*
* 获得新闻的日期
*/
private
String
getNewsDate
(
NodeFilter
dateFilter
,
Parser
parser
)
{
String
newsDate
=
null
;
try
{
NodeList
dateList
=
(
NodeList
)
parser
.
parse
(
dateFilter
)
;
for
(
int
i
=
0
;
i
<
dateList
.
size
(
)
;
i
++
)
{
Span
dateTag
=
(
Span
)
dateList
.
elementAt
(
i
)
;
newsDate
=
dateTag
.
getStringText
(
)
;
}
}
catch
(
ParserException
ex
)
{
Logger
.
getLogger
(
SohuNews
.
class
.
getName
(
)
)
.
log
(
Level
.
SEVERE
,
null
,
ex
)
;
}
return
newsDate
;
}
/**
* 获取新闻的内容
* @param newsContentFilter
* @param parser
* @return content 新闻内容
*/
private
String
getNewsContent
(
NodeFilter
newsContentFilter
,
Parser
parser
)
{
String
content
=
null
;
StringBuilder
builder
=
new
StringBuilder
(
)
;
try
{
NodeList
newsContentList
=
(
NodeList
)
parser
.
parse
(
newsContentFilter
)
;
for
(
int
i
=
0
;
i
<
newsContentList
.
size
(
)
;
i
++
)
{
Div
newsContenTag
=
(
Div
)
newsContentList
.
elementAt
(
i
)
;
builder
=
builder
.
append
(
newsContenTag
.
getStringText
(
)
)
;
}
content
=
builder
.
toString
(
)
;
//转换为String 类型。
if
(
content
!=
null
)
{
parser
.
reset
(
)
;
parser
=
Parser
.
createParser
(
content
,
"gb2312"
)
;
StringBean
sb
=
new
StringBean
(
)
;
sb
.
setCollapse
(
true
)
;
parser
.
visitAllNodesWith
(
sb
)
;
content
=
sb
.
getStrings
(
)
;
// String s = "\";} else{ document.getElementById('TurnAD444').innerHTML = \"\";} } showTurnAD444(intTurnAD444); }catch(e){}";
content
=
content
.
replaceAll
(
"\\\".*[a-z].*\\}"
,
""
)
;
content
=
content
.
replace
(
"[我来说两句]"
,
""
)
;
}
else
{
System
.
out
.
println
(
"没有得到新闻内容!"
)
;
}
}
catch
(
ParserException
ex
)
{
Logger
.
getLogger
(
SohuNews
.
class
.
getName
(
)
)
.
log
(
Level
.
SEVERE
,
null
,
ex
)
;
}
return
content
;
}
/**
* 根据提供的URL,获取此URL对应网页所有的纯文本信息,次方法得到的信息不是很纯,
*常常会得到我们不想要的数据。不过如果你只是想得到某个URL 里的所有纯文本信息,该方法还是很好用的。
* @param url 提供的URL链接
* @return RL对应网页的纯文本信息
* @throws ParserException
* @deprecated 该方法被 getNewsContent()替代。
*/
@Deprecated
public
String
getText
(
String
url
)
throws
ParserException
{
StringBean
sb
=
new
StringBean
(
)
;
//设置不需要得到页面所包含的链接信息
sb
.
setLinks
(
false
)
;
//设置将不间断空格由正规空格所替代
sb
.
setReplaceNonBreakingSpaces
(
true
)
;
//设置将一序列空格由一个单一空格所代替
sb
.
setCollapse
(
true
)
;
//传入要解析的URL
sb
.
setURL
(
url
)
;
//返回解析后的网页纯文本信息
return
sb
.
getStrings
(
)
;
}
/**
* 对新闻URL进行解析提取新闻,同时将新闻插入到数据库中。
* @param content
*/
public
void
parser
(
String
url
)
{
try
{
parser
=
new
Parser
(
url
)
;
NodeFilter
titleFilter
=
new
TagNameFilter
(
"h1"
)
;
NodeFilter
contentFilter
=
new
AndFilter
(
new
TagNameFilter
(
"div"
)
,
new
HasAttributeFilter
(
"id"
,
"sohu_content"
)
)
;
NodeFilter
newsdateFilter
=
new
AndFilter
(
new
TagNameFilter
(
"span"
)
,
new
HasAttributeFilter
(
"class"
,
"c"
)
)
;
NodeFilter
newsauthorFilter
=
new
AndFilter
(
new
TagNameFilter
(
"div"
)
,
new
HasAttributeFilter
(
"class"
,
"editUsr"
)
)
;
String
newsTitle
=
getTitle
(
titleFilter
,
parser
)
;
parser
.
reset
(
)
;
//记得每次用完parser后,要重置一次parser。要不然就得不到我们想要的内容了。
String
newsContent
=
getNewsContent
(
contentFilter
,
parser
)
;
System
.
out
.
println
(
newsContent
)
;
//输出新闻的内容,查看是否符合要求
parser
.
reset
(
)
;
String
newsDate
=
getNewsDate
(
newsdateFilter
,
parser
)
;
parser
.
reset
(
)
;
String
newsauthor
=
getNewsAuthor
(
newsauthorFilter
,
parser
)
;
//先设置新闻对象,让新闻对象里有新闻内容。
setN
ews
(
newsTitle
,
newsauthor
,
newsContent
,
newsDate
,
url
)
;
//将新闻添加到数据中。
this
.
newsToDataBase
(
)
;
}
catch
(
ParserException
ex
)
{
Logger
.
getLogger
(
SohuNews
.
class
.
getName
(
)
)
.
log
(
Level
.
SEVERE
,
null
,
ex
)
;
}
}
//单个文件测试网页
public
static
void
main
(
String
[
]
args
)
{
SohuNews
news
=
new
SohuNews
(
)
;
news
.
parser
(
"http://news.sohu.com/20090518/n264012864.shtml"
)
;
}
}
|
存放新闻的数据库用的是MySql 建表语句如下:(其实不用数据库也可以的,在SohuNews类中注释掉那行红色的代码就可以了,所有得到的新闻都会在后台打印的。)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
CREATE
DATABASE
IF NOT EXISTS
sohunews;
USE
sohunews;
--
-- Definition of table `news`
--
DROP
TABLE
IF EXISTS
`news`;
CREATE
TABLE
`news`
(
`newsid`
int(11)
NOT NULL
auto_increment,
`newstitle`
varchar(60)
NOT NULL,
`newsauthor`
varchar(20)
NOT NULL,
`newscontent`
text
NOT NULL,
`newsurl`
char(130)
NOT NULL,
`newsdate`
varchar(24)
NOT NULL,
PRIMARY KEY
(`newsid`)
)
ENGINE
=
InnoDB
DEFAULT
CHARSET
=utf8;
|
以上的代码写的很粗糙,项目中使用到了HtmlParser工具包,如果需要可以到http://sourceforge.net/projects/htmlparser 网站上下载。如果有需要这个
这篇文章只是一篇抛砖引玉的文章,希望懂爬虫的你能够给点意见,大家交流交流!!
项目源代码:SohuNews
ps:http://www.blogjava.net/gml520/archive/2009/05/20/271691.html