0


使用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解析工具。
因为我们需要执行自己的业务逻辑,所以需要实现此接口。

importus.codecraft.webmagic.*;importus.codecraft.webmagic.processor.PageProcessor;importorg.openqa.selenium.Cookie;importjava.util.Set;publicclassMyPageProcessorimplementsPageProcessor{privateSet<Cookie> cookies =null;//用来存储cookie信息/**
     * 解析返回的数据page
     * @param page Downloader实现类下载的结果。
     */@Overridepublicvoidprocess(Page page){//向Pipeline对象中设置输出结果,把解析的结果放到ResultItems中     
        page.putField("html", page.getHtml().all());}//Site对象可以对爬虫进行一些配置配置,包括编码、抓取间隔、超时时间、重试次数等。privatefinalSite site =newSite().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);//设置超时时间@OverridepublicSitegetSite(){if(cookies!=null&&!cookies.isEmpty()){//将获取到的cookie信息添加到webmagic中for(Cookie cookie : cookies){
                site.addCookie(cookie.getName(),cookie.getValue());}}return site;}//执行业务逻辑publicstaticvoidmain(String[] args){Spider.create(newMyPageProcessor())// 初始访问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请求去后台获取数据,给用户显示出来。
因此,我们的程序只能模拟浏览器去访问动态页面,等待浏览器执行完所有的数据请求之后,再将页面解析出来进行处理。

importorg.openqa.selenium.chrome.ChromeDriver;importorg.openqa.selenium.chrome.ChromeOptions;importorg.openqa.selenium.remote.RemoteWebDriver;importus.codecraft.webmagic.downloader.Downloader;importus.codecraft.webmagic.*;importus.codecraft.webmagic.selector.PlainText;importorg.openqa.selenium.Cookie;importjava.util.Map;publicclassMyDownloaderimplementsDownloader{//声明驱动privateRemoteWebDriver driver;publicMyDownloader(){//第一个参数是使用哪种浏览器驱动,第二个参数是浏览器驱动的地址System.setProperty("webdriver.chrome.driver","C:\\Users\\Administrator\\AppData\\Local\\Google\\Chrome\\Application\\chromedriver.exe");//创建浏览器参数对象ChromeOptions chromeOptions =newChromeOptions();// 设置为 无界面浏览器 模式,若是不想看到浏览器打开,就可以配置此项// chromeOptions.addArguments("--headless");     
        chromeOptions.addArguments("--window-size=1440,1080");// 设置浏览器窗口打开大小this.driver =newChromeDriver(chromeOptions);//创建驱动}/**
     * 由于selenium的默认域名为data;因此第一次必须跳转到登录页,才能加入对应域名
     * @param request Request 
     */@OverridepublicPagedownload(Request request,Task task){try{
            driver.get(request.getUrl());//第一次打开url,跳转到登录页            Thread.sleep(3000);//等待打开浏览器//获取从process返回的site携带的cookies,填充后第二次打开urlSite site = task.getSite();if(site.getCookies()!=null){for(Map.Entry<String,String> cookieEntry : site.getCookies().entrySet()){Cookie cookie =newCookie(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();}returnnull;}@OverridepublicvoidsetThread(int threadNum){}//构建page返回对象privatePagecreatePage(String url,String content){Page page =newPage();
        page.setRawText(content);
        page.setUrl(newPlainText(url));
        page.setRequest(newRequest(url));
        page.setDownloadSuccess(true);return page;}}

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

3.Pipeline

Pileline是抽取结束后,进行数据处理的部分,它主要用于抽取结果的保存,也可以定制Pileline可以实现一些通用的功能。
在这里我们可以指定输出的位置,可以是控制台也可以是文件,当然也可以用户自定义Pipeline实现数据导入到数据库中。

importus.codecraft.webmagic.ResultItems;importus.codecraft.webmagic.Task;importus.codecraft.webmagic.pipeline.Pipeline;publicclassMyPipelineimplementsPipeline{@Overridepublicvoidprocess(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文档保存@SneakyThrowspublicvoidhtml2doc(String fileName,String content){Document docAll =Jsoup.parse(content);//解析网页得到文档对象        com.lowagie.text.Document document =newcom.lowagie.text.Document(PageSize.A4);// 设置纸张大小// 建立一个书写器(Writer)与document对象关联,通过书写器(Writer)可以将文档写入到磁盘中// ByteArrayOutputStream baos = new ByteArrayOutputStream();File file =newFile("D:\\"+ fileName +".doc");RtfWriter2.getInstance(document,newFileOutputStream(file));
        document.open();//打开word文档Elements contexts = docAll.getElementsByTag("p");//获取正文内容ExecutorService executorService =Executors.newFixedThreadPool(10);LinkedList<Object> list =newLinkedList<>();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();}returnnull;});
                list.add(future);}else{Paragraph paragraph =newParagraph(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 instanceofParagraph){
                document.add((Paragraph) o);}if(o instanceofFuture){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();}//处理下载的图片publicImagehandleImage(Element image)throwsIOException,DocumentException{// // 添加图片 Image.getInstance即可以放路径又可以放二进制字节流//图片路径String src = image.attr("data-origin");BufferedInputStream in =Jsoup.connect(src).ignoreContentType(true).maxBodySize(8000000).execute().bodyStream();//注意设置最大下载size,避免图片只能下载一半ByteArrayOutputStream out =newByteArrayOutputStream();byte[] buf =newbyte[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;}}
标签: java 开发语言

本文转载自: https://blog.csdn.net/qq_40610003/article/details/126604534
版权归原作者 晓风残月淡 所有, 如有侵权,请联系我们删除。

“使用webmagic和selenium爬取动态网页”的评论:

还没有评论