DynamicGecco实现爬取规则的动态加载
程序员文章站
2022-05-23 22:27:36
...
Gecco爬虫框架中的SpiderBean
Gecco是一个快速爬虫开发框架,能让开发人员快速的将爬取下来的页面转换为一个简单的java bean。所有的java bean都需要继承同一个接口SpiderBean。根据返回数据格式的不同可以将SpiderBean分成两个子接口——HtmlBean和JsonBean。SpiderBean的定义通常如下:
@Gecco(matchUrl="...")
public class TestHtmlBean implements HtmlBean {
@HtmlField(cssPath=".title")
private String title;
public void setTitle(String title) {
this.title=title;
}
public String getTitle() {
return title;
}
}
或
@Gecco(matchUrl="...")
pulbic class TestJsonBean implements JsonBean {
@JSONPath("$.title")
private String title;
public void setTitle(String title) {
this.title=title;
}
public String getTitle() {
return title;
}
}
详细的Gecco框架使用手册可以参考这里。
为什么要动态生成SpiderBean
- 已经定义了ORM(如:hiberante)的bean,将注解动态的加载到ORM的bean中,可以很方便的将页面格式化后入库
- 很多类似的网站的抓取,SpiderBean都一样,只是提取元素的cssPath不一样,为了不构建很多重复的SpiderBean,可以考虑动态生成SpiderBean
- 通过配置的方式抓取页面,通过后台管理系统、配置文件等配置抓取规则,动态的将配置规则转换成SpiderBean
- 利用动态SpiderBean可以构建可视化爬虫,利用可视化工具构建抓取规则,将规则动态转换为SpiderBean
动态生成SpiderBean的注解
这里介绍bean已经存在的情况下,如何动态的将注解注入到bean中,代码如下:
//动态增加注解
DynamicGecco.html("com.geccocrawler.gecco.demo.dynamic.MyGithub", false)
.gecco("https://github.com/{user}/{project}", "consolePipeline")
.existField("title").htmlField(".repository-meta-content").text(false).build()
.existField("star").htmlField(".pagehead-actions li:nth-child(2) .social-count").text(false).build()
.existField("fork").htmlField(".pagehead-actions li:nth-child(3) .social-count").text().build()
.existField("contributors").htmlField("ul.numbers-summary > li:nth-child(4) > a").href().build()
.existField("request").request().build()
.existField("user").requestParameter("user").build()
.existField("project").requestParameter().build()
.register();
以上动态注解的添加等同于:
@Gecco(matchUrl="https://github.com/{user}/{project}", pipelines="consolePipeline")
public class MyGithub implements HtmlBean {
@Request
private HttpRequest request;
@RequestParameter("user")
private String user;
@RequestParameter("project")
private String project;
@Text(own=false)
@HtmlField(cssPath=".repository-meta-content")
private String title;
@Text(own=false)
@HtmlField(cssPath=".pagehead-actions li:nth-child(2) .social-count")
private int star;
@Text
@HtmlField(cssPath=".pagehead-actions li:nth-child(3) .social-count")
private int fork;
@Href
@HtmlField(cssPath="ul.numbers-summary > li:nth-child(4) > a")
private String contributors;
...setter/getter...
}
注意:这种情况下,由于要对SpiderBean的注解进行动态生成,所有不要将动态生成注解的方法放到任何SpiderBean类中,最好单独写一个新的类用来生成动态注解。
动态生成类、属性和注解
这里介绍的是如何在没有任何Bean的情况下动态生成SpiderBean的全部内容,代码如下:
DynamicGecco.html()
.gecco("https://github.com/{user}/{project}", "consolePipeline")
.stringField("title", FieldType.stringType).htmlField(".repository-meta-content").text(false).build()
.intField("star", FieldType.intType).htmlField(".pagehead-actions li:nth-child(2) .social-count").text(false).build()
.intField("fork", FieldType.intType).htmlField(".pagehead-actions li:nth-child(3) .social-count").text().build()
.register();
以上方法等同于创建了一个这样的类:
@Gecco(matchUrl="https://github.com/{user}/{project}", pipelines="consolePipeline")
public class MyGithub implements HtmlBean {
@Text(own=false)
@HtmlField(cssPath=".repository-meta-content")
private String title;
@Text(own=false)
@HtmlField(cssPath=".pagehead-actions li:nth-child(2) .social-count")
private int star;
@Text
@HtmlField(cssPath=".pagehead-actions li:nth-child(3) .social-count")
private int fork;
...setter/getter...
}
JsonPipeline的使用
由于编译器不知道运行时生成的SpiderBean的存在,这种请求我们通常将SpiderBean转为JSONObject来进行处理,通过继承JsonPipeline实现:
public class ProductListJsonPipeline extends JsonPipeline {
@Override
public void process(JSONObject productList) {
HttpRequest currRequest = HttpGetRequest.fromJson(productList.getJSONObject("request"));
//下一页继续抓取
int currPage = productList.getIntValue("currPage");
int nextPage = currPage + 1;
int totalPage = productList.getIntValue("totalPage");
if(nextPage <= totalPage) {
String nextUrl = "";
String currUrl = currRequest.getUrl();
if(currUrl.indexOf("page=") != -1) {
nextUrl = StringUtils.replaceOnce(currUrl, "page=" + currPage, "page=" + nextPage);
} else {
nextUrl = currUrl + "&" + "page=" + nextPage;
}
SchedulerContext.into(currRequest.subRequest(nextUrl));
}
}
}
Demo
全部demo位于源码下的com.geccocrawler.gecco.demo.dynamic包下,请感兴趣的同学自行下载。