WebMagic爬虫Demo尝试(二) - 多页面
上篇记录了第一个Demo,使用WebMagic进行了单页面的信息获取,在控制台输出了信息,这次来进行多页面的信息获取,然后存储到数据库,使用Mybatis框架,mysql5.5库
pom.xml,以及log4j的配置参见上文
这里记录mybatis-config.xml的配置以及数据库地址的配置
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3307/webmagic
jdbc.username=root
jdbc.password=123456
创建jdbc.propertise文件,写入自己的数据库配置信息,用于连接数据库
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 这里用于获取连接数据库的配置文件 -->
<properties resource="jdbc.properties"></properties>
<!-- 别名配置 -->
<typeAliases>
<package name="pojo"/>
</typeAliases>
<!-- 属性资源配置 -->
<!-- SqlSessionFactory配置 -->
<environments default="development">
<environment id="development">
<!-- 事务管理器配置 -->
<transactionManager type="JDBC"/>
<!-- 数据源配置 -->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mapper/****_titleUrl_oneDao.xml"/>
<mapper resource="mapper/****_user_messageDao.xml"/>
</mappers>
</configuration>
配置MyBatis,配置数据源,配置映射的Mapper文件
Mybatis的相关资料以及配置不多赘述,百度很多,直接进入爬虫逻辑类
与上次的单页面信息获取一样,流程依然是:
下载页面 -> 解析页面信息 -> 得到信息处理信息
只不过这次我们需要获取的是多个页面,而不是一个,获取的是多条信息,
为了方便起见,还是先把可能用到的信息初始化了出来,不考虑安全性性能,拿出来用就好
private static ****_titleUrl_one ****;
private static ****_titleUrl_oneService ****Service = new ****_titleUrl_oneService();
private List<****_titleUrl_one> allList;
private static String username = "dog250";//需要爬取的用户名信息,可更改,也可设置为手动输入(实现控制台的scanner)
private static int count = 0;//文章总数
private static int number = 1;//当前页码数
private static Spider spider = Spider.create(new getCsdn_TitleAndUrl());
private static String START_URL = "https://blog.****.net/" + username + "/article/list/" + number;
private Site site = Site.me()
.setDomain("www.baidu.com")
.setSleepTime(5000)
.setCharset("utf-8")
.setRetrySleepTime(3)
.setTimeOut(1000)//设置超时
.setUserAgent("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_2) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.65 Safari/537.31");
这次初始化了一个Service类,主要是需要进行数据持久化存储
爬取解析逻辑方面的代码:
public void process(Page page) {
allList = new ArrayList<****_titleUrl_one>();
List<String> title = page.getHtml().xpath("*[@id=\"mainBox\"]/main/div[2]/div/h4/a/text()").all();//文章标题信息
List<String> url = page.getHtml().xpath("*[@id=\"mainBox\"]/main/div[2]/div/h4/a").links().all();//文章标题URL
for(int i = 0;i<title.size();i++){
**** = new ****_titleUrl_one();
****.setTitle(title.get(i));
****.setUrl(url.get(i));
allList.add(****);
}
number++;
if(allList.size()!=0){
int num = ****Service.insertList(allList);
System.out.println("共存入数据为:" + num + "条");
}
page.addTargetRequests(doListUrl());//进行下一页的跳转
}
一个页面有多个标题和url信息,所以存入了一个list,然后循环list做数据库的存储,
方法下方一个page.addTargetRequests()方法,这个方法是爬取队列中下一页信息,需要传入一个list参数
这个list里面包含的就是待爬取的页面url,我这里偷懒直接手动组装了一个url集合,代码如下:
/**
* 手工生成listUrl表
*/
public List<String> doListUrl(){
List<String> list = new ArrayList<String>();
for(int i = 2;i<=79;i++) {
list.add("https://blog.****.net/" + username + "/article/list/" + i);
}
return list;
}
因为这个博主的博客列表总共就有79页,所以爬取79页就好.
关于Mybatis的相关类也贴上来:
pojo类:
package pojo;
public class ****_titleUrl_one {
private int id;
private String title;
private String url;
public ****_titleUrl_one(int id, String title, String url) {
this.id = id;
this.title = title;
this.url = url;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
@Override
public String toString() {
return "****_titleUrl_one{" +
"id=" + id +
", title='" + title + '\'' +
", url='" + url + '\'' +
'}';
}
public ****_titleUrl_one() {
}
}
Service类:
package mapper;
import Util.SqlsessionFactory;
import org.apache.ibatis.session.SqlSession;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
import pojo.****_titleUrl_one;
@Service
public class ****_titleUrl_oneService {
private ****_titleUrl_oneDao ****_titleUrl_oneDao;
private SqlSession session;
public ****_titleUrl_oneService(){
session = SqlsessionFactory.getSessionAutoConmit();
****_titleUrl_oneDao = session.getMapper(****_titleUrl_oneDao.class);
}
@Resource
public int insert(****_titleUrl_one pojo){
return ****_titleUrl_oneDao.insert(pojo);
}
public int insertList(List< ****_titleUrl_one> pojos){
return ****_titleUrl_oneDao.insertList(pojos);
}
public List<****_titleUrl_one> select(****_titleUrl_one pojo){
return ****_titleUrl_oneDao.select(pojo);
}
public int update(****_titleUrl_one pojo){
return ****_titleUrl_oneDao.update(pojo);
}
}
DAO:
package mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import pojo.****_titleUrl_one;
public interface ****_titleUrl_oneDao {
int insert(@Param("pojo") ****_titleUrl_one pojo);
int insertList(@Param("pojos") List< ****_titleUrl_one> pojo);
List<****_titleUrl_one> select(@Param("pojo") ****_titleUrl_one pojo);
int update(@Param("pojo") ****_titleUrl_one pojo);
}
最后是Mapper文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="mapper.****_titleUrl_oneDao">
<!--auto generated Code-->
<resultMap id="AllColumnMap" type="pojo.****_titleUrl_one">
<result column="id" property="id"/>
<result column="title" property="title"/>
<result column="url" property="url"/>
</resultMap>
<!--auto generated Code-->
<sql id="all_column">
id,
title,
url
</sql>
<!--auto generated Code-->
<insert id="insert">
INSERT INTO ****_titleUrl_one
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="pojo.id != null"> id, </if>
<if test="pojo.title != null"> title, </if>
<if test="pojo.url != null"> url, </if>
</trim>
VALUES
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="pojo.id != null"> #{pojo.id}, </if>
<if test="pojo.title != null"> #{pojo.title}, </if>
<if test="pojo.url != null"> #{pojo.url}, </if>
</trim>
</insert>
<!--auto generated Code-->
<insert id="insertList">
INSERT INTO ****_titleUrl_one(
<include refid="all_column"/>
)VALUES
<foreach collection="pojos" item="pojo" index="index" separator=",">
(
#{pojo.id},
#{pojo.title},
#{pojo.url}
)
</foreach>
</insert>
<!--auto generated Code-->
<update id="update">
UPDATE ****_titleUrl_one
<set>
<if test="pojo.id != null"> id = #{pojo.id}, </if>
<if test="pojo.title != null"> title = #{pojo.title}, </if>
<if test="pojo.url != null"> url = #{pojo.url} </if>
</set>
WHERE id = #{pojo.id}
</update>
<!--auto generated Code-->
<select id="select" resultMap="AllColumnMap">
SELECT <include refid="all_column"/>
FROM ****_titleUrl_one
<where>
<if test="pojo.id != null"> AND id = #{pojo.id} </if>
<if test="pojo.title != null"> AND title = #{pojo.title} </if>
<if test="pojo.url != null"> AND url = #{pojo.url} </if>
</where>
LIMIT 1000
</select>
<!--auto generated Code-->
<delete id="delete">
DELETE FROM ****_titleUrl_one where id = #{pojo.id}
</delete>
</mapper>
最后贴上爬虫类全代码:
package WebMagicFor****;
import mapper.****_titleUrl_oneService;
import pojo.****_titleUrl_one;
import us.codecraft.webmagic.Page;
import us.codecraft.webmagic.Site;
import us.codecraft.webmagic.Spider;
import us.codecraft.webmagic.processor.PageProcessor;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
/**
* 这个类用于演示,测试WebMagic爬虫在爬取多页面,根据页面结构,以及页面跳转的URL进行翻页下一页爬取的操作,
*
* 对于下一页数据的获取方面,****博客的页面底部的按钮没有找到相应的跳转URL,或许是通过js封装跳转的,导致直接获取链接无法完成
*
* 所有使用了一个比较笨的方法,自己手动查看了所有的页数,然后自己根据URL拼接完成了待爬取的页面URL.
*
* 待优化的部分:
* 当前获取页码的形式为写死的,是自己观看了博客页数以后进行的数据获取,
*
* 优化方法:可以在第一次进入页面的时候获取页面底部的最后一页按钮的text文本,转换为int类型,然后赋予全局变量,获取到总页数(待实现)
*/
/**
*2018.9.26更新,整合mybatis,使用mysql5.5对查询出来的数据做了存储
*/
public class getCsdn_TitleAndUrl implements PageProcessor {
private static ****_titleUrl_one ****;
private static ****_titleUrl_oneService ****Service = new ****_titleUrl_oneService();
private List<****_titleUrl_one> allList;
private static String username = "dog250";//需要爬取的用户名信息,可更改,也可设置为手动输入(实现控制台的scanner)
private static int count = 0;//文章总数
private static int number = 1;//当前页码数
private static Spider spider = Spider.create(new getCsdn_TitleAndUrl());
private static String START_URL = "https://blog.****.net/" + username + "/article/list/" + number;
private Site site = Site.me()
.setDomain("www.baidu.com")
.setSleepTime(5000)
.setCharset("utf-8")
.setRetrySleepTime(3)
.setTimeOut(1000)//设置超时
.setUserAgent("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_2) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.65 Safari/537.31");
@Override
public void process(Page page) {
allList = new ArrayList<****_titleUrl_one>();
List<String> title = page.getHtml().xpath("*[@id=\"mainBox\"]/main/div[2]/div/h4/a/text()").all();//文章标题信息
List<String> url = page.getHtml().xpath("*[@id=\"mainBox\"]/main/div[2]/div/h4/a").links().all();//文章标题URL
for(int i = 0;i<title.size();i++){
**** = new ****_titleUrl_one();
****.setTitle(title.get(i));
****.setUrl(url.get(i));
allList.add(****);
}
number++;
if(allList.size()!=0){
int num = ****Service.insertList(allList);
System.out.println("共存入数据为:" + num + "条");
}
page.addTargetRequests(doListUrl());//进行下一页的跳转
}
@Override
public Site getSite() {
return site;
}
public static void main(String[] args){
spider
.thread(20)//线程数控制在10以内差不多,否则极易出现read time out
.addUrl(START_URL)
.run();
}
/**
* 手工生成listUrl表
*/
public List<String> doListUrl(){
List<String> list = new ArrayList<String>();
for(int i = 2;i<=79;i++) {
list.add("https://blog.****.net/" + username + "/article/list/" + i);
}
return list;
}
public static void ioWrite(String str,int number) throws Exception {
File file = new File("D:" + File.separator+ "WebMagic/****_dog250" + number +".txt");
OutputStream out = null;
try {
out = new FileOutputStream(file);
byte[] data = str.getBytes();
out.write(data);
}catch (Exception e){
e.printStackTrace();
}finally {
out.close();
}
}
}
以及最后运行后存入到数据库的数据: