Scala学习之爬豆瓣电影

news/2024/7/19 12:42:23 标签: scala, java, 爬虫

简单使用Scala和Jsoup对豆瓣电影进行爬虫,技术比較简单易学。

写文章不易,欢迎大家採我的文章,以及给出实用的评论,当然大家也能够关注一下我的github;多谢。

爬虫前期准备">1、爬虫前期准备

  1. 找好须要抓取的链接:https://movie.douban.com/tag/%E7%BB%8F%E5%85%B8?start=20&type=T
  2. 观看该链接的源代码,找到须要进行解析的地方如本实例:图中标明了须要提取的字段。


    1

  3. 下载Jsoup的jar包文件:https://jsoup.org/download
  4. 建立Scalaproject,并将Jsoup的jar包增加project

2、Jsoup简介:

      Jsoup学习请看这个网址:jsoup Cookbook(中文版):http://www.open-open.com/jsoup/
      我这里仅仅介绍我用到了的四个函数:

1、第一个函数:Jsoup.connect(url)
val doc:Document=Jsoup.connect(url).get()//从一个站点获取和解析一个HTML文档,使用get方式。

说的直白点这里获得的就是网页的源代码; //特殊使用:带有參数并使用Post方式 Document doc = Jsoup.connect("http://example.com") .data("query", "Java") .userAgent("Mozilla") .cookie("auth", "token") .timeout(3000) .post(); 2、第二个函数:Element.select(String selector) doc.select("a.nbg")//通过使用CSS(或Jquery)selector syntax 获得你想要操作元素,这里获得的是说有class=nbg的<a/>标签。

3、第三个函数:public String attr(String attributeKey) Elements中的attr函数是通过属性获得Element中第一个匹配该属性的值。如elem.select("a.nbg").attr("title"):获得a标签中的title。 4、第四个函数:public String html() 获得element中包括的Html内容

3、解析Html:

      这里的Html内容比較简单。仅仅须要获得如图一中标记的四处。这里仅仅要用到第二章中的后面三个方法。

//解析Document,须要对比网页源代码进行解析
def parseDoc(doc: Document, movies: ConcurrentHashMap[String, String]) = {
  var count = 0
  for (elem <- doc.select("tr.item")) {//获得全部的电影条目
    movies.put(elem.select("a.nbg").attr("title"), elem.select("a.nbg").attr("title") + "\t" //标题
      + elem.select("a.nbg").attr("href") + "\t" //豆瓣链接
      // +elem.select("p.pl").html+"\t"//简介
      + elem.select("span.rating_nums").html + "\t" //评分
      + elem.select("span.pl").html //评论数
    )
    count += 1
  }
  count
}

4、建立连接获得相应Url的Html

      这里使用了Scala中的Try语法,我这里仅仅简单说明,当Jsoup.connect(url).get() 返回异常时模式匹配会匹配Failure(e)并将异常赋值给模板类中的e。当返回成功时将匹配Success(doc),并将获得的Html的Document赋值给doc。

scala">//用于记录总数。和失败次数
val sum, fail: AtomicInteger = new AtomicInteger(0)
javadoc">/**
  *  当出现异常时10s后重试,异常反复100次
  * javadoctag">@param delay:延时时间
  * javadoctag">@param url:抓取的Url
  * javadoctag">@param movies:存取抓到的内容
  */
def requestGetUrl(times: Int = 100, delay: Long = 10000)(url: String, movies: ConcurrentHashMap[String, String]): Unit = {
  Try(Jsoup.connect(url).get()) match {//使用try来推断是否成功和失败对网页进行抓取
    case Failure(e) =>
      if (times != 0) {
        println(e.getMessage)
        fail.addAndGet(1)
        Thread.sleep(delay)
        requestGetUrl(times - 1, delay)(url, movies)
      } else throw e
    case Success(doc) =>
      val count = parseDoc(doc, movies);
      if (count == 0) {
        Thread.sleep(delay);
        requestGetUrl(times - 1, delay)(url, movies)
      }
      sum.addAndGet(count);
  }
}

5、使用并发集合

      为了加快住区速度使用了Scala中的并发集合:par。相似于java中的fork/join框架;

scala">javadoc">/**
  * 多线程抓取
  * javadoctag">@param url:原始的Url
  * javadoctag">@param tag:电影标签
  * javadoctag">@param maxPage:页数
  * javadoctag">@param threadNum:线程数
  * javadoctag">@param movies:并发集合存取抓到的内容
  */
def concurrentCrawler(url: String, tag: String, maxPage: Int, threadNum: Int, movies: ConcurrentHashMap[String, String]) = {
  val loopPar = (0 to maxPage).par
  loopPar.tasksupport = new ForkJoinTaskSupport(new ForkJoinPool(threadNum)) // 设置并发线程数
  loopPar.foreach(i => requestGetUrl()(url.format(URLEncoder.encode(tag, "UTF-8"), 20 * i), movies)) // 利用并发集合多线程同步抓取:遍历全部页
  saveFile1(tag, movies)//保存为文件
}

6、运行任务:

      想要进行爬虫仅仅须要这样调用concurrentCrawler(URL, tag, page, Thread_Num, new ConcurrentHashMapString, String)函数即可。

def main(args: Array[String]): Unit = {
    val Thread_Num = 30 //指定并发运行线程数
    val t1 = System.currentTimeMillis
    for ((tag, page) <- tags)
      concurrentCrawler(URL, tag, page, Thread_Num, new ConcurrentHashMap[String, String]())//并发抓取
    val t2 = System.currentTimeMillis
    println(s"抓取数:$sum  重试数:$fail  耗时(秒):" + (t2 - t1) / 1000)
  }
}

运行结果:
抓取数:793 重试数:0 耗时(秒):4
01
02

本文来自伊豚wpeace(blog.wpeace.cn)

7、全部代码:

scala">import java.io.{File, PrintWriter}
import java.net.URLEncoder
import java.text.SimpleDateFormat
import java.util.Date
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.atomic.AtomicInteger

import org.jsoup.Jsoup
import org.jsoup.nodes.Document

import scala.collection.JavaConversions._
import scala.collection.mutable.ArrayBuffer
import scala.collection.parallel.ForkJoinTaskSupport
import scala.concurrent.forkjoin.ForkJoinPool
import scala.util.{Failure, Success, Try}

javadoc">/**
  * Created by peace on 2017/3/5.
  */
object Douban {
  val URL = "https://movie.douban.com/tag/%s?

scala">start=%d&type=T"

scala"> //訪问的链接 //须要抓取的标签和页数 val tags = Map( "经典" -> 4, //tag,页数 "爱情" -> 4, "动作" -> 4, "剧情" -> 4, "悬疑" -> 4, "文艺" -> 4, "搞笑" -> 4, "战争" -> 4 ) //解析Document,须要对比网页源代码进行解析 def parseDoc(doc: Document, movies: ConcurrentHashMap[String, String]) = { var count = 0 for (elem <- doc.select("tr.item")) { movies.put(elem.select("a.nbg").attr("title"), elem.select("a.nbg").attr("title") + "\t" //标题 + elem.select("a.nbg").attr("href") + "\t" //豆瓣链接 // +elem.select("p.pl").html+"\t"//简介 + elem.select("span.rating_nums").html + "\t" //评分 + elem.select("span.pl").html //评论数 ) count += 1 } count } //用于记录总数。和失败次数 val sum, fail: AtomicInteger = new AtomicInteger(0) javadoc">/** * 当出现异常时10s后重试,异常反复100次 * javadoctag">@param delay:延时时间 * javadoctag">@param url:抓取的Url * javadoctag">@param movies:存取抓到的内容 */ def requestGetUrl(times: Int = 100, delay: Long = 10000)(url: String, movies: ConcurrentHashMap[String, String]): Unit = { Try(Jsoup.connect(url).get()) match {//使用try来推断是否成功和失败对网页进行抓取 case Failure(e) => if (times != 0) { println(e.getMessage) fail.addAndGet(1) Thread.sleep(delay) requestGetUrl(times - 1, delay)(url, movies) } else throw e case Success(doc) => val count = parseDoc(doc, movies); if (count == 0) { Thread.sleep(delay); requestGetUrl(times - 1, delay)(url, movies) } sum.addAndGet(count); } } javadoc">/** * 多线程抓取 * javadoctag">@param url:原始的Url * javadoctag">@param tag:电影标签 * javadoctag">@param maxPage:页数 * javadoctag">@param threadNum:线程数 * javadoctag">@param movies:并发集合存取抓到的内容 */ def concurrentCrawler(url: String, tag: String, maxPage: Int, threadNum: Int, movies: ConcurrentHashMap[String, String]) = { val loopPar = (0 to maxPage).par loopPar.tasksupport = new ForkJoinTaskSupport(new ForkJoinPool(threadNum)) // 设置并发线程数 loopPar.foreach(i => requestGetUrl()(url.format(URLEncoder.encode(tag, "UTF-8"), 20 * i), movies)) // 利用并发集合多线程同步抓取:遍历全部页 saveFile1(tag, movies) } //直接输出 def saveFile(file: String, movies: ConcurrentHashMap[String, String]) = { val writer = new PrintWriter(new File(new SimpleDateFormat("yyyyMMdd").format(new Date()) + "_" + file ++ ".txt")) for ((_, value) <- movies) writer.println(value) writer.close() } // 排序输出到文件 def saveFile1(file: String, movies: ConcurrentHashMap[String, String]) = { val writer = new PrintWriter(new File(new SimpleDateFormat("yyyyMMdd").format(new Date()) + "_" + file ++ ".txt")) val col = new ArrayBuffer[String](); for ((_, value) <- movies) col += value; val sort = col.sortWith( (o1, o2) => { val s1 = o1.split("\t")(2); val s2 = o2.split("\t")(2); if (s1 == null || s2 == null || s1.isEmpty || s2.isEmpty) { true } else { s1.toFloat > s2.toFloat } } ) sort.foreach(writer.println(_)) writer.close() } def main(args: Array[String]): Unit = { val Thread_Num = 30 //指定并发运行线程数 val t1 = System.currentTimeMillis for ((tag, page) <- tags) concurrentCrawler(URL, tag, page, Thread_Num, new ConcurrentHashMap[String, String]())//并发抓取 val t2 = System.currentTimeMillis println(s"抓取数:$sum 重试数:$fail 耗时(秒):" + (t2 - t1) / 1000) } }

http://www.niftyadmin.cn/n/837063.html

相关文章

java字节字符流代码_JAVA 一点字节流与字符流的笔记

最近用Java写了一个网页数据采集的程序, 偶尔发现出现少量中文乱码的现象. 后来才知道对于中文要用字符流来进行操作.以下是采用字节流的代码:/*** Get the target URLs source* Use byte stream** param url* target URL* return String: source* throws Exception*/publicsta…

html5原生自带的日期控件和时间控件

直接上代码&#xff1a; <!doctype html> <html> <head><meta charset"UTF-8"><title>html5原生自带的日期控件和时间控件</title><script src"http://code.jquery.com/jquery-latest.js"></script><s…

Ubuntu 18.04安装网易云音乐(转载)

作为Ubuntu下唯一一款超级好用的音乐软件&#xff0c;必须下载。 提升为root权限后操作 0 : 网易云音乐1.0.0(该版本较为好安装)下载地址 http://s1.music.126.net/download/pc/netease-cloud-music_1.0.0_amd64_ubuntu16.04.deb 1 : 将网易云音乐文件放到家目录 cp /home/ghz/…

Python从菜鸟到高手(1):初识Python

1 Python简介 1.1 什么是Python Python是一种面向对象的解释型计算机程序设计语言&#xff0c;由荷兰人吉多范罗苏姆&#xff08;Guido van Rossum&#xff09;于1989年发明&#xff0c;第一个公开发行版发行于1991年。目前Python的最新发行版是Python3.6。 Python是纯粹的自由…

Nginx在Windows平台的配置与使用

一、Nginx的概述 1、什么是Nginx: Nginx (engine x) 是一个高性能的HTTP和反向代理服务器&#xff0c;也是一个IMAP/POP3/SMTP服务器。Nginx是由伊戈尔赛索耶夫为俄罗斯访问量第二的Rambler.ru站点&#xff08;俄文&#xff1a;Рамблер&#xff09;开发的&#xff0c;第…

被遗忘的条件编译

预处理程序提供了条件编译的功能。可以按不同的条件去编译不同的程序部分&#xff0c;因而产生不同的目标代码文件&#xff0c;这对程序的移植和调试是很有用的。条件编译有三种形式&#xff1a; 第一种形式: #ifdef 标识符 程序段 1 #else 程序段 2 #endif 第二种形式&#xf…

实时分布式具备高容错性的计算系统storm------核心topology架构图和storm运行topology的工作流程图

文章来源&#xff1a;http://blog.csdn.net/yangbutao/article/details/8445630 hadoop一般用在离线的分析计算中&#xff0c;而storm区别于hadoop&#xff0c;用在实时的流式计算中&#xff0c;被广泛用来进行实时日志处理、实时统计、实时风控等场景&#xff0c;当然也可以用…

streamsets 集成 cratedb 测试

我们可以集成crate 到streamsets 中可以实现强大的数据导入&#xff0c;数据分析能力。 演示的是进行csv 文件的解析并输出到cratedb 环境使用docker && docker-compose 环境启动 docker-compose yaml 文件version: "3" services:sets:image: streamsets/da…