1.网络爬虫是一个自动提取网页的程序,它为搜索引擎从万维网上下载网页,是搜索引擎的重要组成。传统爬虫从一个或若干初始网页的URL开始,获得初始网页上的URL,在抓取网页的过程中,不断从当前页面上抽取新的URL放入队列,直到满足系统的一定停止条件。
2.那么程序获取网页的原理到底是怎么回事呢?看下面的图:客服端首先向服务器端发出Http请求,之后服务器端返回相应的结果或者请求超时客户端自己报错。
服务器端发出的Http请求,实际上说是对服务器的文件的请求。下面的表格是一些常见的HTTP请求对应的文件。(因为第一列给出的都是主机的网址信息,主机一般都通过配置文件将该请求转换为网站主页地址index.php或index.jsp或者index.html等)
3.Java爬虫的代码实现
1)dbcp.properties
`########DBCP配置文件##########
#驱动名
driverClassName=com.mysql.jdbc.Driver
#url
url=jdbc:mysql://127.0.0.1:3306/peoper?useSSL=false
#用户名
username=root
#密码
password=sbj174615
#初试连接数
initialSize=30
#最大活跃数
maxTotal=30
#最大idle数
maxIdle=10
#最小idle数
minIdle=5
#最长等待时间(毫秒)
maxWaitMillis=1000
#程序中的连接不使用后是否被连接池回收(该版本要使用removeAbandonedOnMaintenance和removeAbandonedOnBorrow)
#removeAbandoned=true
removeAbandonedOnMaintenance=true
removeAbandonedOnBorrow=true
#连接在所指定的秒数内未使用才会被删除(秒)(为配合测试程序才配置为1秒)
removeAbandonedTimeout=1
2)DBCPUtil.java
package com.j1702.db;
import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.commons.dbcp2.BasicDataSourceFactory;
/**
* DBCP配置类
* @author SUN
*/
public class DBCPUtil {
private static Properties properties = new Properties();
private static DataSource dataSource;
//加载DBCP配置文件
static{
try{
properties.load(DBCPUtil.class.getClassLoader().getResourceAsStream("dbcp.properties"));
dataSource = BasicDataSourceFactory.createDataSource(properties);
}catch(Exception e){
e.printStackTrace();
}
}
//从连接池中获取一个连接
//从连接池中获取一个连接
public static Connection getConnection(){
Connection connection = null;
try {
connection = dataSource.getConnection();
connection.setAutoCommit(false);
} catch (SQLException e) {
e.printStackTrace();
}
return connection;
}
}
3)java爬虫
package com.rimi.pachong;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.j1702.db.DBCPUtil;
public class pachong1 {
public static void main(String[] args) {
pachong1 videoLinkGrab = new pachong1();
videoLinkGrab.saveData("http://www.80s.tw/movie/list/q----");
}
/**
* 将获取到的数据保存在数据库中
*
* @param baseUrl
* 爬虫起点
* @return null
* */
public void saveData(String baseUrl) {
Map<String, Boolean> oldMap = new LinkedHashMap<String, Boolean>(); // 存储链接-是否被遍历
Map<String, String> videoLinkMap = new LinkedHashMap<String, String>(); // 视频下载链接
String oldLinkHost = ""; // host
Pattern p = Pattern.compile("(https?://)?[^/\\s]*"); // 比如:http://www.zifangsky.cn
Matcher m = p.matcher(baseUrl);
if (m.find()) {
oldLinkHost = m.group();
}
oldMap.put(baseUrl, false);
videoLinkMap = crawlLinks(oldLinkHost, oldMap);
// 遍历,然后将数据保存在数据库中
try {
Connection connection=DBCPUtil.getConnection();
for (Map.Entry<String, String> mapping : videoLinkMap.entrySet()) {
String sql ="insert into movie(MovieName,MovieLink) values(?,?)";
PreparedStatement ptmt =connection.prepareStatement(sql);
ptmt.setString(1, mapping.getKey());
ptmt.setString(2, mapping.getValue());
ptmt.execute();
connection.commit();
// System.out.println(mapping.getKey() + " : " + mapping.getValue());
}
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 抓取一个网站所有可以抓取的网页链接,在思路上使用了广度优先算法 对未遍历过的新链接不断发起GET请求, 一直到遍历完整个集合都没能发现新的链接
* 则表示不能发现新的链接了,任务结束
*
* 对一个链接发起请求时,对该网页用正则查找我们所需要的视频链接,找到后存入集合videoLinkMap
*
* @param oldLinkHost
* 域名,如:http://www.zifangsky.cn
* @param oldMap
* 待遍历的链接集合
*
* @return 返回所有抓取到的视频下载链接集合
* */
private Map<String, String> crawlLinks(String oldLinkHost,
Map<String, Boolean> oldMap) {
Map<String, Boolean> newMap = new LinkedHashMap<String, Boolean>(); // 每次循环获取到的新链接
Map<String, String> videoLinkMap = new LinkedHashMap<String, String>(); // 视频下载链接
String oldLink = "";
for (Map.Entry<String, Boolean> mapping : oldMap.entrySet()) {
// System.out.println("link:" + mapping.getKey() + "--------check:"
// + mapping.getValue());
// 如果没有被遍历过
if (!mapping.getValue()) {
oldLink = mapping.getKey();
// 发起GET请求
try {
URL url = new URL(oldLink);
HttpURLConnection connection = (HttpURLConnection) url
.openConnection();
connection.setRequestMethod("GET");
// connection.setConnectTimeout(6000);
// connection.setReadTimeout(6000);
if (connection.getResponseCode() == 200) {
InputStream inputStream = connection.getInputStream();
BufferedReader reader = new BufferedReader(
new InputStreamReader(inputStream, "UTF-8"));
String line = "";
Pattern pattern = null;
Matcher matcher = null;
//电影详情页面,取出其中的视频下载链接,不继续深入抓取其他页面
if(isMoviePage(oldLink)){
boolean checkTitle = false;
String title = "";
while ((line = reader.readLine()) != null) {
//取出页面中的视频标题
if(!checkTitle){
pattern = Pattern.compile("([^\\s]+).*?</title>");
matcher = pattern.matcher(line);
if(matcher.find()){
title = matcher.group(1);
checkTitle = true;
continue;
}
}
// 取出页面中的视频下载链接
pattern = Pattern
.compile("(thunder:[^\"]+).*thunder[rR]es[tT]itle=\"[^\"]*\"");
matcher = pattern.matcher(line);
if (matcher.find()) {
videoLinkMap.put(title,matcher.group(1));
System.out.println("视频名称: "
+ title + " ------ 视频链接:"
+ matcher.group(1));
break; //当前页面已经检测完毕
}
}
}
//电影列表页面
else if(checkUrl(oldLink)){
while ((line = reader.readLine()) != null) {
pattern = Pattern
.compile("<a href=\"([^\"\\s]*)\"");
matcher = pattern.matcher(line);
while (matcher.find()) {
String newLink = matcher.group(1).trim(); // 链接
// 判断获取到的链接是否以http开头
if (!newLink.startsWith("http")) {
if (newLink.startsWith("/"))
newLink = oldLinkHost + newLink;
else
newLink = oldLinkHost + "/" + newLink;
}
// 去除链接末尾的 /
if (newLink.endsWith("/"))
newLink = newLink.substring(0,
newLink.length() - 1);
// 去重,并且丢弃其他网站的链接
if (!oldMap.containsKey(newLink)
&& !newMap.containsKey(newLink)
&& (checkUrl(newLink) || isMoviePage(newLink))) {
System.out.println("temp: " + newLink);
newMap.put(newLink, false);
}
}
}
}
reader.close();
inputStream.close();
}
connection.disconnect();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
oldMap.replace(oldLink, false, true);
}
}
// 有新链接,继续遍历
if (!newMap.isEmpty()) {
oldMap.putAll(newMap);
videoLinkMap.putAll(crawlLinks(oldLinkHost, oldMap)); // 由于Map的特性,不会导致出现重复的键值对
}
return videoLinkMap;
}
/**
* 判断是否是2015年的电影列表页面
* @param url 待检查URL
* @return 状态
* */
public boolean checkUrl(String url){
Pattern pattern = Pattern.compile("http://www.80s.tw/movie/list/q----\\d*");
Matcher matcher = pattern.matcher(url);
if(matcher.find())
return true; //2015年的列表
else
return false;
}
/**
* 判断页面是否是电影详情页面
* @param url 页面链接
* @return 状态
* */
public boolean isMoviePage(String url){
Pattern pattern = Pattern.compile("http://www.80s.tw/movie/\\d+");
Matcher matcher = pattern.matcher(url);
if(matcher.find())
return true; //电影页面
else
return false;
}
}