后端项目开发:爬取动态网页(webmagic和selenium)

这篇具有很好参考价值的文章主要介绍了后端项目开发:爬取动态网页(webmagic和selenium)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

爬取网页一般是用Python的PhantomJS比较多,当然java也可以爬网页,主要是靠Chrome-Headless(无头浏览器)模拟浏览器爬取网页的,该项目由google公司维护,相比于PhantomJS,拥有更好的性能及效率。

使用java的话,需要加入webmagic和selenium的maven依赖包实现网页的获取。

  <!--爬取网页-->
<dependency>
    <groupId>us.codecraft</groupId>
    <artifactId>webmagic-core</artifactId>
    <version>0.7.4</version>
</dependency>
<dependency>
    <groupId>us.codecraft</groupId>
    <artifactId>webmagic-extension</artifactId>
    <version>0.7.4</version>
</dependency>
<!--selenium依赖-->
<dependency>
    <groupId>org.seleniumhq.selenium</groupId>
    <artifactId>selenium-java</artifactId>
    <version>3.13.0</version>
</dependency>

WebMagic的有四大组件:

1.PageProcessor

PageProcessor接口负责解析页面,抽取有用信息,以及发现新的链接。WebMagic使用Jsoup作为HTML解析工具。
因为我们需要执行自己的业务逻辑,所以需要实现此接口。

import us.codecraft.webmagic.*;
import us.codecraft.webmagic.processor.PageProcessor;
import org.openqa.selenium.Cookie;
import java.util.Set;

public class MyPageProcessor implements PageProcessor {    
    private Set<Cookie> cookies = null;//用来存储cookie信息
    
     /**
     * 解析返回的数据page
     * @param page Downloader实现类下载的结果。
     */
    @Override
    public void process(Page page) {
    	//向Pipeline对象中设置输出结果,把解析的结果放到ResultItems中 	
        page.putField("html", page.getHtml().all());
    }
    
    //Site对象可以对爬虫进行一些配置配置,包括编码、抓取间隔、超时时间、重试次数等。
    private final Site site = new Site()
            .addHeader("user-agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36")//添加header信息,当对方网站识别爬虫的时候,需要填写
            .setDomain("example.com")//输入你要爬的网页域名,不带http和https前缀
            .addCookie("token","auth")//通过F12看后台自己cookie的token值,填进去
            .setTimeOut(2000);//设置超时时间

    @Override
    public Site getSite() {
        if(cookies!=null && !cookies.isEmpty()){
            //将获取到的cookie信息添加到webmagic中
            for (Cookie cookie : cookies) {
                site.addCookie(cookie.getName(),cookie.getValue());
            }
        }
        return site;
    }
    //执行业务逻辑
    public static void main(String[] args) {
        Spider.create(new MyPageProcessor())
                // 初始访问url地址
                .addUrl("https://www.baidu.com")
                //.setDownloader(new MyDownloader())//可选择使用自定义的
                //.addPipeline(new MyPipeline())  //自定义的Pipeline,不设置的话,信息自动打印到console界面上              
                .run();// 执行爬虫
    }
}

2.Downloader

Downloader接口负责从互联网上下载页面,以便后续处理。
一般若是抓取静态界面,仅仅使用上面的PageProcessor 的实现类就够了。但是,若是需要抓取动态页面的话,这样就不够了,尤其是现在很多网页都是vue或react建设的。
在上面的例子中,我们请求某个页面,只会获得静态的页面,没有数据在里面。这是因为我们只是获得了某个url返回的html文档。
一般,真实环境的访问获取到html文档后,还要执行多个api请求去后台获取数据,给用户显示出来。
因此,我们的程序只能模拟浏览器去访问动态页面,等待浏览器执行完所有的数据请求之后,再将页面解析出来进行处理。

import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.remote.RemoteWebDriver;
import us.codecraft.webmagic.downloader.Downloader;
import us.codecraft.webmagic.*;
import us.codecraft.webmagic.selector.PlainText;
import org.openqa.selenium.Cookie;
import java.util.Map;

public class MyDownloader implements Downloader {
    //声明驱动
    private RemoteWebDriver driver;
    
    public MyDownloader() {
        //第一个参数是使用哪种浏览器驱动,第二个参数是浏览器驱动的地址
        System.setProperty("webdriver.chrome.driver","C:\\Users\\Administrator\\AppData\\Local\\Google\\Chrome\\Application\\chromedriver.exe");
        //创建浏览器参数对象
        ChromeOptions chromeOptions = new ChromeOptions();
        // 设置为 无界面浏览器 模式,若是不想看到浏览器打开,就可以配置此项
        // chromeOptions.addArguments("--headless");     
        chromeOptions.addArguments("--window-size=1440,1080");// 设置浏览器窗口打开大小
        this.driver = new ChromeDriver(chromeOptions); //创建驱动
    }

     /**
     * 由于selenium的默认域名为data;因此第一次必须跳转到登录页,才能加入对应域名
     * @param request Request 
     */
    @Override
    public Page download(Request request, Task task) {
        try {
            driver.get(request.getUrl());//第一次打开url,跳转到登录页            
            Thread.sleep(3000);//等待打开浏览器
            //获取从process返回的site携带的cookies,填充后第二次打开url
            Site site = task.getSite();
            if (site.getCookies() != null) {
                for (Map.Entry<String, String> cookieEntry : site.getCookies()
                        .entrySet()) {
                    Cookie cookie = new Cookie(cookieEntry.getKey(),
                            cookieEntry.getValue());
                    driver.manage().addCookie(cookie);
                }
                //添加对应domain的cookie后,第二次打开url
                driver.get(request.getUrl());
            }
            Thread.sleep(2000);
            driver.executeScript("window.scrollTo(0, document.body.scrollHeight - 1000)");//需要滚动到页面的底部,获取完整的数据
            Thread.sleep(2000);//等待滚动完成
            //获取页面,打包成Page对象,传给PageProcessor 实现类
            Page page = createPage(request.getUrl(), driver.getPageSource());            
            //driver.close();//看需要是否关闭浏览器
            return page;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public void setThread(int threadNum) {

    }

    //构建page返回对象
    private Page createPage(String url, String content) {
        Page page = new Page();
        page.setRawText(content);
        page.setUrl(new PlainText(url));
        page.setRequest(new Request(url));
        page.setDownloadSuccess(true);
        return page;
    }
}

可能自己的电脑上没有chrome的驱动,下载地址如下:
http://chromedriver.storage.googleapis.com/index.html
解压到对应的路径就好了

3.Pipeline

Pileline是抽取结束后,进行数据处理的部分,它主要用于抽取结果的保存,也可以定制Pileline可以实现一些通用的功能。
在这里我们可以指定输出的位置,可以是控制台也可以是文件,当然也可以用户自定义Pipeline实现数据导入到数据库中。文章来源地址https://www.toymoban.com/news/detail-705882.html

import us.codecraft.webmagic.ResultItems;
import us.codecraft.webmagic.Task;
import us.codecraft.webmagic.pipeline.Pipeline;

public class MyPipeline implements Pipeline {
    @Override
    public void process(ResultItems resultItems, Task task) {
        List<String> title1 = resultItems.get("title");
        List<String> content = resultItems.get("content");
        String substring = title1.get(0).substring(48, title1.get(0).indexOf("<!---->"));
        String fileName = StringUtils.trim(substring);
        html2doc(fileName, content.get(0));
    }
    //将html转换成word文档保存
    @SneakyThrows
    public void html2doc(String fileName, String content) {
        Document docAll = Jsoup.parse(content);//解析网页得到文档对象        
        com.lowagie.text.Document document = new com.lowagie.text.Document(PageSize.A4);// 设置纸张大小
        // 建立一个书写器(Writer)与document对象关联,通过书写器(Writer)可以将文档写入到磁盘中
        // ByteArrayOutputStream baos = new ByteArrayOutputStream();
        File file = new File("D:\\" + fileName + ".doc");
        RtfWriter2.getInstance(document, new FileOutputStream(file));
        document.open();//打开word文档
        Elements contexts = docAll.getElementsByTag("p");//获取正文内容
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        LinkedList<Object> list = new LinkedList<>();
        for (Element context : contexts) {
            if (context.html().contains("img")) {
                Future<Image> future = executorService.submit(
                        () -> {
                            try {
                                Image img = handleImage(context.select("img").get(0));
                                return img;
                            } catch (IOException | DocumentException e) {
                                e.printStackTrace();
                            }
                            return null;
                        }
                );
                list.add(future);
            } else {                
                Paragraph paragraph = new Paragraph(context.text());//  文本正文                
                paragraph.setAlignment(com.lowagie.text.Element.ALIGN_LEFT);// 正文格式左对齐
                paragraph.setSpacingBefore(5);// 离上一段落(标题)空的行数                
                paragraph.setFirstLineIndent(20);// 设置第一行空的列数
                list.add(paragraph);
            }
        }
        executorService.shutdown();
        try {
            executorService.awaitTermination(1, TimeUnit.DAYS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        for (Object o : list) {
            if (o instanceof Paragraph) {
                document.add((Paragraph) o);
            }
            if (o instanceof Future) {
                Image image = ((Future<Image>) o).get();
                document.add(image);
                //以下内容是将下载的图片保存到本地
                //FileOutputStream fout = new FileOutputStream("D:\\img\\" + image.hashCode() + ".png");                
                //fout.write(image.getRawData());//将字节写入文件
                //fout.close();
            }
        }
        document.close();
    }
    //处理下载的图片
    public Image handleImage(Element image) throws IOException, DocumentException {
        // // 添加图片 Image.getInstance即可以放路径又可以放二进制字节流
        //图片路径
        String src = image.attr("data-origin");
        BufferedInputStream in = Jsoup.connect(src).ignoreContentType(true).maxBodySize(8000000).execute().bodyStream();//注意设置最大下载size,避免图片只能下载一半
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        byte[] buf = new byte[8192];
        int length = 0;
        while ((length = in.read(buf, 0, buf.length)) != -1) {
            out.write(buf, 0, length);
        }
        Image img = Image.getInstance(out.toByteArray());
        img.setAbsolutePosition(0, 0);
        img.setAlignment(Image.LEFT);// 设置图片显示位置
        img.scaleAbsolute(500, 300);// 直接设定显示尺寸
        in.close();
        out.close();
        return img;
    }
}

到了这里,关于后端项目开发:爬取动态网页(webmagic和selenium)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 后端项目开发:整合redis缓存

    因为各种场合比如门户和后台,需要不同的redis配置,我们在common包配置通用的reids配置。 1.新建service目录,建立RedisService服务接口,同时编写工具类实现该接口。 2.在config目录下,编写redis通用配置 3.其他包只需要继承该基础配置就够了

    2024年02月11日
    浏览(31)
  • 后端项目开发:整合全局异常处理

    新建exception目录,用来进行自定义的全局异常处理。 (1)新建自定义的GlobalException基 类继承RuntimeException类,我们自定义的异常类全部需要继承GlobalException基类进行处理。 这里我们直接利用之前定义的错误码接口类。 (2)在exception目录下,新建GlobalExceptionHandler类,拦截异

    2024年02月11日
    浏览(36)
  • 后端项目开发:集成Druid数据源

    Druid作为连接池中间件可以监控数据库访问性能,对数据库密码加密,查看SQL执行日志,扩展JDBC。 添加依赖 参考官方文档:https://github.com/alibaba/druid/wiki 更多配置参考:https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter 启动应用:访问http://localhost:8001/druid/login.html查看监

    2024年02月11日
    浏览(43)
  • 后端项目开发:工具类封装(序列化、反射)

    根据《阿里巴巴开发规范》,包名使用单数,类名可以使用复数。 所以generic-common创建util包和utils工具类 很多时候我们需要将接收到的json数据转换为对象,或者将对象转为json存储。这时候我们需要编写用于json转换的工具类。 新建util目录,再创建JacksonUtils类

    2024年02月11日
    浏览(35)
  • 后端项目开发:集成接口文档(swagger-ui)

    swagger集成文档具有功能丰富、及时更新、整合简单,内嵌于应用的特点。 由于后台管理和前台接口均需要接口文档,所以在工具包构建BaseSwaggerConfig基类。 1.引入依赖 2.需要添加Swagger配置类。 将需要配置的字段提取出来,单独作为一类 前台接口和后台管理的包的配置,只需

    2024年02月11日
    浏览(37)
  • 后端项目开发:分页功能的实现(Mybatis+pagehelper)

    分页查询是项目中的常用功能,此处我们基于Mybatis对分页查询进行处理。 引入分页依赖 在http目录下,新建PageResult类,我们用此类包装分页结果。

    2024年02月11日
    浏览(41)
  • 遇到跨端开发或多项目开发时,遇到的一些问题探讨,后端开发语言如何选择?

    ​最近有同学问我,做后端开发项目时用php,java,c#,go,pathon...哪个好,从最近阿里云、美团服务器崩溃来看,我想给你最直接的回答是,没有完美的,只有适合自己的。咱们讨论最多的问题就是跨多端开发,以及多项目开发后期所带来的升级、维护等相关问题,接下来就

    2024年02月04日
    浏览(45)
  • 【python】Flask网页开发——论坛项目实战(完整代码)

    笔记为自我总结整理的学习笔记,若有错误欢迎指出哟~ 【论坛项目实战】 【python】Flask网页开发——论坛项目实战(完整代码) 【python】Flask网页开发——论坛项目实战(1.导航条实现) 【python】Flask网页开发——论坛项目实战(2.登录与注册) 【python】Flask网页开发——论

    2024年04月28日
    浏览(42)
  • Linux的开发环境安装配置与后端项目部署

    目录 一.安装开发环境 1.准备阶段 1.1 创建新目录 1.2 解压文件 2.JDK的安装与配置环境变量 2.1 解压jdk压缩包 2.2 配置环境变量 2.3 设置环境变量生效  2.4 验证是否安装成功 3.Tomcat的安装与使用 3.1 解压安装 3.2 开启服务 3.3 开放端口   3.4 访问成功 4.MySQL的安装 4.1 检查卸载原有

    2024年02月06日
    浏览(44)
  • 【Dolphinscheduler3.1.1】二次开发本地启动项目(前端+后端)

    由于业务的定制化开发,需要对Dolphinscheduler进行二次开发,现将项目的启动步骤记录如下。 Maven: v3.5+,配阿里云仓库地址即可 Node: v16.+ MySQL (5.7系列) : 两者任选其一即可 JDK (1.8+) : 必装 ZooKeeper (3.7.1) :必装 ZooKeeper安装步骤如下 官网下载压缩包并解压到D:Program Files (x86)zoo

    2024年01月21日
    浏览(50)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包