本文中使用的 HttpClient 是在 apache HttpClient 的基础上封装的 class, 文章末尾会给出源码分享
大体思路就是: 首先我们用 HttpClient 模拟请求, 获取到 html 代码, 用 jsoup 解析过滤 html, 获取我们想要的数据
因为我要抓取这个网站所有分页的数据, 我在模拟请求之前, 在浏览器地址栏改变 url 中的分页参数, 发现无法请求? 这就很麻烦了, 没有办法去请求了!
后来请教领导之后发现这个网站是通过 过滤器 实现了一个 防盗链
大致代码如下:
- public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
- ServletException {
- HttpServletRequest req = (HttpServletRequest) request;
- HttpServletResponse resp = (HttpServletResponse) response;
- String referer = req.getHeader("referer");
- // 下面的 IP 地址是正常页面请求
- if (null != referer && (referer.trim().startsWith("http://localhost:8033") || referer.trim().startsWith("http://www.finereporthelp.com/test/hello.html"))) {
- System.out.println("正常页面请求" + referer);
- chain.doFilter(req, resp);
- // 下面的就是出现不是正常页面请求的时候跳转
- } else {
- System.out.println("盗链" + referer);
- req.getRequestDispatcher("/LdapLogin.jsp").forward(req, resp);
- }
- }
防盗链的作用限制你只能在浏览器端通过类似
window.location.href
这种请求去访问, 禁止其他方式请求, 所以我们只需要在请求的时候模拟这个参数就 ok 了!
下面看核心代码
- maven
- <!-- https://mvnrepository.com/artifact/org.jsoup/jsoup -->
- <dependency>
- <groupId>org.jsoup</groupId>
- <artifactId>jsoup</artifactId>
- <version>1.11.2</version>
- </dependency>
- <dependency>
- <groupId>org.apache.httpcomponents</groupId>
- <artifactId>httpclient</artifactId>
- <version>4.5.1</version>
- </dependency>
- <!-- https://mvnrepository.com/artifact/commons-httpclient/commons-httpclient -->
- <dependency>
- <groupId>commons-httpclient</groupId>
- <artifactId>commons-httpclient</artifactId>
- <version>3.1</version>
- </dependency>
模拟请求
- public void catchHy88() throws Exception {
- for (int i = 1; i < 508; i++) {
- log.info("循环开始, 当前索引:" + i);
- String url = "http://www.abc.com/search.html?kw = 大连金州 & type=company&page=PAGE_NUM/";
- HttpClient httpClient = new HttpClient(url.replace("PAGE_NUM", Integer.toString(i)));
- // 设置 referer 参数, 绕过防盗链
- httpClient.setReferer("www.abc.com");
- // 获取 html
- String html = httpClient.request();
- log.info("接收到返回的 html 消息, 开始筛选数据");
- // 处理 html 获取 data
- HY88Converter hy88Converter = HY88Converter.getInstance();
- List < Company > list = hy88Converter.html2Company(html, i);
- // 批量执行数据库操作, 当 List 长度为 N 时
- companyMapper.batchInsert(list);
- }
- }
这时候已经获取 html 代码了, 但是我们如何去过滤呢?
先分析页面结构
页面结构
马赛克部分是我需要的数据, 也就是我们要先获取所有
ul li div[class = conttext]
的部分
Elements elements = doc.select("ul[class=big-pro]").select("li[class=wap]").select("div[class=pro-left]").select("div[class=conttext]");
然后遍历 Elements,jsoup 语法类似 jquery, 相比用正则表达式, 我觉得这种方式更便捷一些吧
下面是我的源码
- import java.util.ArrayList;
- import java.util.List;
- import org.apache.commons.lang3.StringUtils;
- import org.jsoup.Jsoup;
- import org.jsoup.nodes.Document;
- import org.jsoup.nodes.Element;
- import org.jsoup.select.Elements;
- import com.taven.web.hy88crawler.entity.Company;
- public class HY88Converter {
- private static HY88Converter instance = null;
- /**
- * 返回单例
- *
- * @return
- */
- public static HY88Converter getInstance() {
- if (instance == null) instance = new HY88Converter();
- return instance;
- }
- /**
- * 将抓取到的 html 信息转为公司实体
- *
- * @param html
- */
- public List < Company > html2Company(String html, Integer currentPage) {
- List < Company > companyList = new ArrayList < Company > ();
- Document doc = Jsoup.parse(html);
- Elements elements = doc.select("ul[class=big-pro]").select("li[class=wap]").select("div[class=pro-left]").select("div[class=conttext]");
- for (Element element: elements) {
- try {
- String phoneStr = element.select("li[class=com]:contains(电话号码)").text();
- String phone = phoneStr.replace("电话号码", "");
- // 如果包含 - 说明是座机 执行下一次
- if (StringUtils.isBlank(phone) || phone.contains("-")) continue;
- String contact = element.select("li[class=fen]").text();
- if (StringUtils.isBlank(contact)) continue;
- String name = element.select("p[class=p-title]").select("a").attr("title");
- companyList.add(new Company(name, phone, contact, currentPage));
- } catch(Exception e) {
- continue;
- }
- }
- return companyList;
- }
- }
HttpClient 工具类 链接: https://pan.baidu.com/s/1mkl9yL6 密码: hfzv
参考文献
jsoup 中文 api
HttpClient 参考博客, 感谢原作者
来源: http://www.jianshu.com/p/19fb5ec33b99