• java爬虫进阶 —— ip池使用,iframe嵌套,异步访问破解


    写之前稍微说一下我对爬与反爬关系的理解

    一、什么是爬虫
          爬虫英文是splider,也就是蜘蛛的意思,web网络爬虫系统的功能是下载网页数据,进行所需数据的采集。主体也就是根据开始的超链接,下载解析目标页面,这时有两件事,一是把相关超链接继续往容器内添加,二是解析页面目标数据,不断循环,直到没有url解析为止。举个栗子:我现在要爬取苏宁手机价格数据,爬取思路就是拿到第一页的url作为蜘蛛网的中心点开始,爬取当页的手机详情页面的价格信息以及下一页的url并添加进容器,这样循环往复只要存放url容器里有就会一直往下机械执行,直到尾页没有下一页,这就是个扩散的过程。

    二、什么是反爬虫以及为什么要反
        反爬虫就是根据请求的一定的访问特征进行特殊处理,比如封Ip,弹验证码,返回不对应信息等等。

        至于反爬的原因大概有几点

        1、爬虫占总PV值高,就相当于一大群僵尸用户在访问你的网站,如果不管制,平白浪费服务器资源

        2、某些则是出于商业竞争问题必须反爬,不让自己的商业信息被对手批量获取。之前看到一个例子很贴切,两个to B公司对外售卖商品,而一家写了一个自动爬取对手网站商品价格并于己方商品价格对比,保持低于一定价格进行动态浮动的脚本,顾客在买之前肯定会对同行业价格进行了解,于是结果你们都懂,对方很快发现这边的公司动的手脚,于是一场哄哄烈烈爬与反爬的较量开始了

        3、还有就是一些无人认领的爬虫,可能用的人都忘了它的存在,一直在辛勤的爬

    三 、常见的一些反爬手段
        1、根据ip访问频率以及数量上限封锁ip,那个用户会一秒访问页面几十次或者连续几小时每隔半秒访问一次,动作很明显,封就完事了。

    2、还有就是加载页面时动态获取,而不是静态数据了。举个栗子,某东的价格信息是动态加载

        3、还有就是主体页面是异步加载嵌套在iframe里面的,并且 src="about:blank" ,这个正常下载下来的页面是没有内容的

        4、故意挖坑,在页面做一些隐藏链接,如果被访问明显是爬进去的爬虫,接下来就是一顿封

        5、后台对方问进行统计,对userAgent进行阈值判断,这个据说容易误伤

        6、还有就是在页面展示上进行做手脚,有的价格信息用图片展示,去哪儿网

        7、Cookie反扒,推荐链接,有一篇不错的介绍传送门

    四、对前三种进行破解
        1.设立ip池循环进行循环访问

         首先我们从西刺等代理网站抓取一些免费的ip,其次进行无效ip过滤,这一步看情况吧,我个人实践是西刺网上得ip时而访问得通时而无效,所以我就去掉过滤得步骤,再就是用这些代理ip进行实际访问。

    public class CsdnReadCount implements PageProcessor {


    // IP地址代理库Map
    private static Map<String, Integer> IPProxyRepository = new HashMap<>();
    private static List<String> keysArray = new ArrayList<>();
    private static int index;

    private Site site = Site
    .me()
    .setDomain("http://www.xicidaili.com/")
    .setSleepTime(3000).setUserAgent("Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36");

    //按照顺序获取一个ip
    public static HttpHost getRandomProxy() {

    // 随机获取host:port,并构建代理对象
    String host = keysArray.get(index);
    if(index<keysArray.size()-1) {
    index+=1;
    }else {
    index = 0;
    }

    int port = IPProxyRepository.get(host);
    HttpHost proxy = new HttpHost(host, port); // 设置http代理
    return proxy;
    }

    //抓取西刺网前两页上代理IP,因为不稳定,所以不做过滤
    public void process(Page page) {

    Html html = page.getHtml();
    List<String> hosts = html.xpath("//div[@id='body']/table/tbody/tr[@class='odd']/td[2]").replace("<td>","").replace("</td>","").all();
    List<String> ports = html.xpath("//div[@id='body']/table/tbody/tr[@class='odd']/td[3]").replace("<td>","").replace("</td>","").all();

    keysArray.addAll(hosts);
    for (int i=0;i<hosts.size();i++){
    IPProxyRepository.put(hosts.get(i),Integer.valueOf(ports.get(i)));
    }
    }

    public Site getSite() {
    return site;
    }

    //请求页面,返回页面html代码
    public static String getHttpContent(String url)throws IOException {

    HttpHost proxy = getRandomProxy();
    CloseableHttpClient httpClient = HttpClients.custom().setProxy(proxy).build(); // 创建httpclient对象


    HttpGet request = new HttpGet(url); // 构建htttp get请求
    request.setHeader("user-agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.221 Safari/537.36 SE 2.X MetaSr 1.0");

    RequestConfig requestConfig = RequestConfig.custom()
    .setConnectTimeout(5000).setConnectionRequestTimeout(1000)
    .setSocketTimeout(5000).build();

    request.setConfig(requestConfig);
    String host = null;
    Integer port = null;
    if (proxy != null) {
    host = proxy.getHostName();
    port = proxy.getPort();
    }

    request.setHeader("user-agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.221 Safari/537.36 SE 2.X MetaSr 1.0");

    try {

    CloseableHttpResponse response = httpClient.execute(request);
    String result = EntityUtils.toString(response.getEntity(), "utf-8");
    System.out.println("ip"+host+"连接成功"+index);
    System.out.println(result);
    System.out.println(new Date());
    }catch (Exception c){
    System.out.println("ip"+host+"连接失败···");
    System.out.println("index:"+index);
    System.out.println(new Date());
    }

    return null;
    }

    public static void main(String[] args) throws InterruptedException,IOException{

    CsdnReadCount csdnReadCount = new CsdnReadCount();
    Spider spider = Spider.create(csdnReadCount);
    spider.addUrl("http://www.xicidaili.com/nn/1");
    spider.addUrl("http://www.xicidaili.com/nn/2");

    spider.run();

    getHttpContent("https://blog.csdn.net/caihaijiang/article/list/1");

    }
    }

        2.异步嵌套iframe的破解

        这里用到了selenium需要引入依赖

        准备: 下载谷歌驱动(谷歌驱动的版本一定要对应谷歌浏览器得版本,否则会报错)驱动下载传送门

                    引入所需依赖  :  

    <dependency>
    <groupId>org.seleniumhq.selenium</groupId>
    <artifactId>selenium-java</artifactId>
    <version>3.3.1</version>
    </dependency>
    package sample;

    import org.openqa.selenium.By;
    import org.openqa.selenium.WebDriver;
    import org.openqa.selenium.WebElement;
    import org.openqa.selenium.chrome.ChromeDriver;

    import java.util.ArrayList;
    import java.util.List;

    public class SeleniumCloudMusic {

    public static void main(String[] args) {


    String key="webdriver.chrome.driver";
    //谷歌驱动在你本地位置
    String value="C:/Users/win 10/Desktop/暂存/chromedriver.exe";
    System.setProperty(key,value);

    WebDriver driver = new ChromeDriver();
    driver.get("http://music.163.com/#");

    WebElement iframe = driver.findElement(By.className("g-iframe"));
    driver.switchTo().frame(iframe);

    List<WebElement> elements = driver.findElements(By.xpath("//div[@class='u-cover u-cover-1']"));
    List<String> playLists = new ArrayList<String>();

    for (WebElement webElement:elements){
    webElement.findElement(By.tagName("div"));
    WebElement node = webElement.findElement(By.tagName("a"));
    String url = node.getAttribute("href");

    System.out.println(url);
    playLists.add(url);

    }

    for (String str:playLists){


    driver.get(str);
    WebElement ifra = driver.findElement(By.className("g-iframe"));
    driver.switchTo().frame(ifra);

    WebElement subject = driver.findElement(By.tagName("h2"));

    System.out.println(subject.getText());
    }
    }
    }
    3.动态加载破解

          此处依旧是某些网站加载数据通过ajax异步加载,你直接爬是没有信息的,所以需要爬取页面数据过程中单独生成请求获取价格数据

    package sample;

    import org.apache.http.client.config.RequestConfig;
    import org.apache.http.client.methods.CloseableHttpResponse;
    import org.apache.http.client.methods.HttpGet;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.impl.client.HttpClients;
    import org.apache.http.util.EntityUtils;
    import us.codecraft.webmagic.Page;
    import us.codecraft.webmagic.Site;
    import us.codecraft.webmagic.Spider;
    import us.codecraft.webmagic.processor.PageProcessor;
    import org.json.JSONArray;
    import org.json.JSONObject;

    public class JDPriceSplider implements PageProcessor {
    private Site site = Site
    .me()
    .setDomain("https://item.jd.com")
    .setSleepTime(3000);

    public void process(Page page) {

    String url = page.getUrl().get();
    String id = url.substring(url.lastIndexOf("/") + 1, url.lastIndexOf("."));
    //拼接获取价格的url
    String priceUrl = "https://p.3.cn/prices/mgets?pduid=1504781656858214892980&skuIds=J_" + id;

    //获取价格json信息
    String priceJson = getHttpContent(priceUrl);
    if (priceJson != null) {
    // 解析json [{"op":"4899.00","m":"9999.00","id":"J_3133843","p":"4799.00"}] 将该json字符串封装成json对象
    if (priceJson.contains("error")) { // 返回{"error":"pdos_captcha"},说明价格url已经不可用,更换pduid再做解析
    } else {
    JSONArray priceJsonArray = new JSONArray(priceJson);
    JSONObject priceJsonObj = priceJsonArray.getJSONObject(0);
    String priceStr = priceJsonObj.getString("p").trim();
    Float price = Float.valueOf(priceStr);


    System.out.println(price);
    }
    }
    }

    //相当于页面进行ajax调用,单独获取价格
    public static String getHttpContent(String url) {

    CloseableHttpClient httpClient = HttpClients.custom().build(); // 创建httpclient对象

    HttpGet request = new HttpGet(url); // 构建htttp get请求
    request.setHeader("user-agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.221 Safari/537.36 SE 2.X MetaSr 1.0");


    RequestConfig requestConfig = RequestConfig.custom()
    .setConnectTimeout(5000).setConnectionRequestTimeout(1000)
    .setSocketTimeout(5000).build();
    request.setConfig(requestConfig);

    try {
    CloseableHttpResponse response = httpClient.execute(request);

    return EntityUtils.toString(response.getEntity());
    } catch (Exception e) {
    e.printStackTrace();
    }

    return null;
    }

    public Site getSite() {
    return site;
    }

    public static void main(String[] args) {

    //访问手机详情页面
    Spider spider = Spider.create(new JDPriceSplider());
    spider.addUrl("https://item.jd.com/6946605.html");
    spider.run();
    }
    }
    ---------------------
    作者:紫荆王朝
    来源:CSDN
    原文:https://blog.csdn.net/wu18296184782/article/details/80269274
    版权声明:本文为博主原创文章,转载请附上博文链接!

  • 相关阅读:
    java中的subList
    值传递和引用传递
    java程序中有异常处理和没有异常处理的区别
    正则表达式
    poj 3187 三角数问题
    poj 2718 切数问题 穷竭搜索
    ACM 广度优化搜索算法总结
    poj 3669 火星撞地球问题 bfs算法
    poj 2251 三维地图最短路径问题 bfs算法
    ACM 深度优化搜索算法小总结
  • 原文地址:https://www.cnblogs.com/yuluoxingkong/p/10019274.html
Copyright © 2020-2023  润新知