JAVA 多线程爬虫实例详解
前言
以前喜欢Python的爬虫是出于他的简洁,但到了后期需要更快,更大规模的爬虫的时候,我才渐渐意识到Java的强大。Java有一个很好的机制,就是多线程。而且Java的代码效率执行起来要比python快很多。这份博客主要用于记录我对多线程爬虫的实践理解。
线程
线程是指一个任务从头至尾的执行流。线程提供了运行一个任务的机制。对于Java而言,可以在一个程序中并发地启动多个线程。这些线程可以在多处理器系统上同时运行。
runnable接口
任务类必须实现runnable接口,它只包含一个run方法。需要实现这个方法来告诉系统线程将如何运行。
Thread类
包含为任务而创建的线程的构造方法,以及控制线程的方法。
synchronized关键字
为避免竞争状态,防止多个线程同时进入程序的某个特定部分,即临界区,以便一次只有一个线程可以访问临界区。
利用加锁同步
Java可以显式加锁,一个锁是一个Lock接口的实例,它定义了加锁和释放锁的方法。
线程池
线程池是管理开发执行任务个数的理想方法。Java提供Executor接口来执行线程池中的任务,提供ExecutorService接口管理和控制任务。
使用线程池的方法获取url列表
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
|
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/*
* 获取京东评论url列表
*/
public class MyThreading {
private static String p_id = null ;
private static Url urls = null ;
public MyThreading(String p_id){
this .p_id = p_id ; // 京东商品的id
urls = new Url(p_id);
}
public List<String> getUriList(){
ExecutorService executor = Executors.newCachedThreadPool();
for ( int i = 0 ; i < 600 ; i ++){
executor.execute( new AddUrl(i)); // 添加任务到线程池
}
executor.shutdown();
while (!executor.isTerminated()){}
return urls.getList();
}
public static class AddUrl implements Runnable{
int page;
public AddUrl( int page){
this .page = page;
}
public void run(){
urls.addList(page); // 启动多线程任务
}
}
public static class Url {
private static Lock lock = new ReentrantLock(); // 开启显式家锁
private static List<String> urlList = new ArrayList();
private String p_id;
public Url(String p_id ){
this .p_id = p_id ;
}
public List<String> getList(){
return urlList;
}
public void addList( int page){
lock.lock();
try {
String url = "http://club.jd.com/productpage/p-" + p_id + "-s-0-t-0-p-" + String.valueOf(page) + ".html" ;
// Thread.sleep(5);
urlList.add(url); //添加url到url列表
} catch (Exception ex ){
}
finally {
lock.unlock(); // 解锁
}
}
}
public static void main(String[] args) {
String p_id = "2441288" ;
MyThreading myThreading = new MyThreading(p_id);
List <String> urlList = myThreading.getUriList();
for (String url : urlList){
System.out.println(url);
}
System.out.println(urlList.size());
}
}
|
代码分析
- 代码的作用:获取京东评论的url列表
- 类的说明:MyThreading是主类, AddUrl和Url是它的内部类,AddUrl实现了runnable的接口,主要启动多线程服务运行Url的addList方法。而Url是最内核的部分 ,他提供addList任务和多线程的共享区域urlList,所以在实现添加url的步骤中,需要对urlList加锁。
- 线程池主要有两种类型,一个是固定线程池,即newFixedThreadPool;另一个是newCachedThreadPool,这个主要利用了缓冲机制,能动态地添加线程。在上述代码中,我主要使用了newCachedthreadPool.
使用线程池的方法根据url列表爬取网页元素
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
|
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class ThreadingCrawel {
private static Content content = null ;
private static List<String> urlList = null ;
public ThreadingCrawel(List<String> urlList){
this .urlList = urlList;
content = new Content();
}
public List<String> getContent(){
ExecutorService executor = Executors.newCachedThreadPool();
for (String url : urlList){
executor.execute( new AddContent(url));
}
executor.shutdown();
while (!executor.isTerminated()){}
return content.getContent();
}
public static class AddContent implements Runnable{
String url;
public AddContent(String url){
this .url = url;
}
public void run(){
content.addContent(url);
}
}
public static class Content {
private static Lock lock = new ReentrantLock();
private static List<String> contentList = new ArrayList();
public void addContent(String url){
String content = "" ;
BufferedReader in = null ;
try {
URL realUrl = new URL(url);
URLConnection connection = realUrl.openConnection();
in = new BufferedReader( new InputStreamReader(connection.getInputStream(), "gbk" ));
String line;
while ( (line = in.readLine()) != null ){
content += line + "\n" ;
}
} catch (Exception e){
e.printStackTrace();
}
finally {
try {
if (in != null ){
in.close();
}
} catch (Exception e2){
e2.printStackTrace();
}
}
Pattern p = Pattern.compile( "content\":\".*?\"" );
Matcher match = p.matcher(content);
String tmp;
lock.lock();
while (match.find()){
tmp = match.group();
tmp = tmp.replaceAll( "\"" , "" );
tmp = tmp.replace( "content:" , "" );
tmp = tmp.replaceAll( "<.*?>" , "" );
contentList.add(tmp);
try {
Thread.sleep( 1 );
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
lock.unlock();
}
public List getContent(){
return contentList;
}
}
public static void main(String[] args){
long start = System.currentTimeMillis();
String p_id = "2441288" ;
MyThreading myThreading = new MyThreading(p_id);
List <String> urlList = myThreading.getUriList();
ThreadingCrawel threadingCrawel = new ThreadingCrawel(urlList);
List <String> contentList = threadingCrawel.getContent();
for (String content : contentList){
System.out.println(content);
}
long end = System.currentTimeMillis();
System.out.println(end - start);
}
}
|
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!
原文链接:http://blog.csdn.net/qq_30843221/article/details/52486735