JAVA 爬虫的几种方法及应用

由于最近业务需要爬取第三方网站的数据,开始学习JAVA爬虫的相关知识,在此记录期间遇到的问题以及对知识进行总结。

爬虫开始


打开浏览器进入目标网站,按F12打开控制台。


1. HTTP

如果能在控制台里直接找到数据接口,那么这是最好的爬取数据方式。点击network,现在大多数网站都是异步请求,所以筛选xhr请求,有的网站在点击分页时会暴露xhr请求需要多试试。例如[https://www.iqiyi.com/r_bodan...]点击分页后可以看到异步请求的url。找到需要的接口后:
(1)使用okhttp/httpclient等工具请求接口。
(2)JSONObject解析返回的json。

/**
 * OkHttpGet请求封装
 */
public static String okHttpGet(String url,int timeOut)throws IOException{
    String result = "";
 OkHttpClient client = new OkHttpClient.Builder()
            .followRedirects(false)
            .followSslRedirects(false)
            .connectTimeout(timeOut, TimeUnit.SECONDS)
            .build();
 Request request = new Request.Builder()
            .url(url)
            .get()
            .build();
 final Call call = client.newCall(request);
 try {
        Response response = call.execute();
 return response.body().string();
 } catch (IOException e) {
       logger.error("n okHttp IOException nurl: {}",url);
 throw new IOException();
 }
}

2. Jsoup

找不到数据接口后,可以用HTTP工具请求目标页获取网页的源码。这时候需要另一个工具JSoup。Jsoup是一款Java的HTML解析器,主要用来对HTML解析。官方文档:https://www.open-open.com/jsoup/

Document doc = Jsoup.connect(url)
        .cookies(cookieMap)
        .header("referer", "https://acz.youku.com/wow/ykpage/act/top_hot?spm=a2ha1.14919748_WEBHOME_GRAY.youku-search-box.5~5~A")
        .timeout(0).get();

doc里的内容就是网页的源码,timeout是等待时间,当超过等待时间时请求就会中断,0表示一直等待。

String imgUrl = doc.select("div.poster-img_2PAaJ").attr("style");

像这样就可以获取class为'poster-img_2PAaJ'的div的style的内容,Jsoup有丰富的解析元素的方法,具体可以查看上述的官方文档。

值得注意的是,有很多网站把源代码的数据放在了script标签里面,还是json格式,如果能把数据源的json提取出来再用JSONObject解析不就快多了嘛。
image.png

Element scriptElement = doc.getElementsByTag("script").get(4);
String[] scriptDataArray =  scriptElement.data().split("var");
String[] videoInfo = scriptDataArray[5].split("= ");
JSONObject videoJsonObject = JSONObject.parseObject(videoInfo[1]);
// 就是用String的split()方法切割出json
// 实际使用时需要防范数组越界问题

Jsoup缺点:并不支持xpath解析。


3. Selenium PhantomJS

以上的两种方法基本可以应对很多网站的爬虫,但是有些网站对爬虫的防范很高,比如:

JAVA 爬虫的几种方法及应用_第1张图片

网页源代码里没有相关数据,用Jsoup也获取不到主页面的元素。这时候就需要Selenium和PhantomJS出场了。
Selenium是一个用于Web应用程序测试的工具,它可以模拟真实的浏览器并模拟用户在操作,它不仅可以用于应用程序测试,在爬虫方面也可以爬取js渲染的网站。
PhantomJS与Selenium的区别主要在于它是一种无界面的浏览器,由于省去了界面加载所以执行速度相比较快,但是由于phantomJS已经停止了更新并且在JAVA版本中的PhantomJS无法执行JS脚本(比如模拟滚动条滚动),所以不会优先考虑。

引入依赖


 org.seleniumhq.selenium
 selenium-java
 3.141.59

Selenium支持许多浏览器驱动比如IE,Chrome,Firefox,看别的博客说火狐支持的最好,那就选火狐吧。
下载geckodriver驱动,并安装firefox,都选最新的就完事。
https://github.com/mozilla/geckodriver

程序自动打开一个火狐浏览器并自动访问目标网页。

DesiredCapabilities desiredCapabilities = new DesiredCapabilities();
//ssl证书支持
desiredCapabilities.setCapability("acceptSslCerts", true);
//css搜索支持
desiredCapabilities.setCapability("cssSelectorsEnabled", true);
//js支持
desiredCapabilities.setJavascriptEnabled(true);
System.setProperty("webdriver.gecko.driver", "D:\\geckodriver-v0.27.0-win64\\geckodriver.exe");
WebDriver driver = new FirefoxDriver();
String href = "https://www.ixigua.com/cinema/filter/dianshiju/?sort=%E6%9C%80%E7%83%AD";
driver.get(href); //访问目标网站
List element = driver.findElements(By.className("lvideo-list__item"))
// 获取元素,类似于Jsoup的元素解析

还可以模拟用户操作,例如点击class名为'a'的元素

Actions action = new Actions(driver);
WebElement element = driver.findElement(By.className("a"));
action.click(element).perform

对于一些滑动加载的页面,可以模拟页面滚动。

WebDriver driver = HttpClientUtil.getFireFoxDriver(false, null); // 包装了一下实例化代码
driver.get(url)
List listItem;
int j = 0;
// 滚动页面直到加载了100个'lvideo-list__item'元素
while (true) {
    ((JavascriptExecutor) driver).executeScript("window.scrollTo(0," + (j * 500) + ")");
 try {
        Thread.sleep(2000);
 } catch (InterruptedException e) {
        e.printStackTrace();
 }
    listItem = driver.findElements(By.className("lvideo-list__item"));
 if (listItem.size() >= 120 || listItem.size() == 0) {
        break;
 }
    j++;
}

当我心满意足的在自己的电脑上爬完数据后,准备放到测试服务器运行,哦豁报错,原来是由于linux没有图形化界面启动不了,一种方法是用phantomJS,但是呢偏偏不维护了而且有bug,后来才发现selenium现在也支持无头(无界面)模式了

FirefoxOptions firefoxOptions = new FirefoxOptions();
firefoxOptions.setHeadless(true); //设置为无头模式
WebDriver driver = new FirefoxDriver(firefoxOptions);
driver.manage().window().maximize();

这样就没问题了。
值得一提的是,driver有两种关闭,driver.close()和driver.quit()两个方法,close()方法意思是关闭当前tab,假设打开了5个tab执行close只会关闭当前tab还剩下4个,当只有一个tab时执行close会关闭浏览器,但这个时候打开进程管理器发现进程还在,所以在最后释放资源的时候用driver.quit()方法,它会直接关闭进程。

}finally{
    driver.quit();
}

你可能感兴趣的:(java,网页爬虫)