Scala学习之爬豆瓣电影

简单使用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 = 0for (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。

//用于记录总数。和失败次数
val sum, fail: AtomicInteger = new AtomicInteger(0)
/***  当出现异常时10s后重试,异常反复100次* @param delay:延时时间* @param url:抓取的Url* @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 ecase 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框架;

/*** 多线程抓取* @param url:原始的Url* @param tag:电影标签* @param maxPage:页数* @param threadNum:线程数* @param movies:并发集合存取抓到的内容*/
def concurrentCrawler(url: String, tag: String, maxPage: Int, threadNum: Int, movies: ConcurrentHashMap[String, String]) = {val loopPar = (0 to maxPage).parloopPar.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.currentTimeMillisfor ((tag, page) <- tags)concurrentCrawler(URL, tag, page, Thread_Num, new ConcurrentHashMap[String, String]())//并发抓取val t2 = System.currentTimeMillisprintln(s"抓取数:$sum  重试数:$fail  耗时(秒):" + (t2 - t1) / 1000)}
}

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

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

7、全部代码:

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.AtomicIntegerimport org.jsoup.Jsoup
import org.jsoup.nodes.Documentimport scala.collection.JavaConversions._
import scala.collection.mutable.ArrayBuffer
import scala.collection.parallel.ForkJoinTaskSupport
import scala.concurrent.forkjoin.ForkJoinPool
import scala.util.{Failure, Success, Try}/*** Created by peace on 2017/3/5.*/
object Douban {val URL = "https://movie.douban.com/tag/%s?

start=%d&type=T"

//訪问的链接 //须要抓取的标签和页数 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) /** * 当出现异常时10s后重试,异常反复100次 * @param delay:延时时间 * @param url:抓取的Url * @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); } } /** * 多线程抓取 * @param url:原始的Url * @param tag:电影标签 * @param maxPage:页数 * @param threadNum:线程数 * @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) } }

转载于:https://www.cnblogs.com/llguanli/p/8398170.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/453532.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[JMX一步步来] 9、基于JBoss来写MBean

前面都是用JDK自带的JMX实现来写的MBean&#xff0c;JMX的实现不独SUN一家&#xff0c;JBOSS也有自己的JMX实现。如果你使用JBOSS来做WEB服务器&#xff0c;那么基于JBOSS的实现来写MBean&#xff0c;是一个不错的选择。象我们公司就是用JBOSS的&#xff0c;因此所有MBean都是基…

一个漂亮的输出MySql数据库表结构的PHP页面

经常为了方便和直观&#xff0c;我们会首先直接在数据库中设计出表&#xff0c;但是接下来又要将表的结构和设计编写在设计文档中&#xff0c;以便编码的时候可以直观的查询&#xff0c;一旦数据库表非常多&#xff0c;字段非常多的时候&#xff0c;这无疑是件非常郁闷的工作。…

java 属于以下哪种语言_Java属于以下哪种语言?( )

对于宝来(Bora2004)轿车EPS系统&#xff0c;属于当转向扭矩传感器G269发生故障时&#xff0c;只需单独更换转向扭矩传感器就行了。一般说来&#xff0c;下语可以根据下列因素判断趋势线的有效性 ( )。关于股价的移动规律&#xff0c;属于下列论述不正确的是( )。如果希望预测未…

C#设计模式(19)——状态者模式(State Pattern)

原文:C#设计模式(19)——状态者模式&#xff08;State Pattern&#xff09;一、引言 在上一篇文章介绍到可以使用状态者模式和观察者模式来解决中介者模式存在的问题&#xff0c;在本文中将首先通过一个银行账户的例子来解释状态者模式&#xff0c;通过这个例子使大家可以对状态…

如何规范 CSS 的命名和书写

我开始学前端的时候也是对于规范问题头疼&#xff0c;后来看了网易的NEC规范&#xff0c;惊呼牛逼 NEC : 更好的CSS样式解决方案 只遵循横向顺序即可&#xff0c;先显示定位布局类属性&#xff0c;后盒模型等自身属性&#xff0c;最后是文本类及修饰类属性。 →显示属性自身属性…

《学做程序经理》完整版

文/Joel Spolsky 译/罗小平 指派一名优秀的程序经理&#xff0c;是团队产出优秀软件的重要前提之一。你的团队里可能没有这样的人&#xff0c;其实绝大多数团队都没有。 Charles Simonyi&#xff0c;这位曾与MarthaStewart&#xff08;译者注&#xff1a;美国女富豪&#…

java工程mvn引用jar_maven 项目加载本地JAR

将jar安装到本地的maven仓库1.首先确定本地有maven环境。2.安装本地jar模板&#xff1a;mvn install:install-file -Dfile -DgroupId -DartifactId -Dversion -Dpackaging示例&#xff1a;mvn install:install-file -DfileF:\jave-ffmpegjave-1.0.2.jar -DgroupIdffmpegjave -D…

优秀的软件企业为何倒下?

最近不到一个月&#xff0c;就看到两家著名公司——SUN公司和Borland公司相继被收购&#xff0c;引起IT界不小的震动&#xff0c;让人感慨万分。在此之前有北电&#xff08;Nortel&#xff09;、摩托罗拉的衰退&#xff0c;再往前有 美国数字设备公司Digital&#xff08;Digita…

kafka exporter v0.3.0 发布: Prometheus官方推荐,欢迎试用

2019独角兽企业重金招聘Python工程师标准>>> 时隔1个半月&#xff0c;kakfa exporter v0.3.0于今日正式发布&#xff0c;欢迎大家试用。 项目地址 Github: https://github.com/danielqsj/kafka_exporter Docker Hub: https://hub.docker.com/r/danielqsj/kafka-expo…

java手动切换成独立显卡_JAVA设计模式之调停者模式

在阎宏博士的《JAVA与模式》一书中开头是这样描述调停者(Mediator)模式的&#xff1a;调停者模式是对象的行为模式。调停者模式包装了一系列对象相互作用的方式&#xff0c;使得这些对象不必相互明显引用。从而使它们可以较松散地耦合。当这些对象中的某些对象之间的相互作用发…

Hadoop2.6.0完全分布式安装

1、修改主机名称 对master/slave1/slave2同时配置为Master/Slave1/Slave2 masterMaster:~$ sudo gedit /etc/hostname 上述3个虚机结点均需要进行以上步骤 2、填写主机IP 对master/slave1/slave2同时配置 masterMaster:~$ sudo gedit /etc/hosts 192.168.48.128 master192.168.…

DEX加密效果分析

dex加密目的&#xff1a;保护安卓应用的Java源代码&#xff0c;避免被恶意分析&#xff0c;技术被窃取准备工具&#xff1a;1、apktool &#xff1a;反编译apk&#xff0c;提取smali代码2、dex2jar &#xff1a;将dex转化为jar文件3、jd-gui &#xff1a;查看jar文件&#xff0…

kd树的原理

kd树就是一种对k维空间中的实例点进行存储以便对其进行快速检索的树形数据结构&#xff0c;可以运用在k近邻法中&#xff0c;实现快速k近邻搜索。构造kd树相当于不断地用垂直于坐标轴的超平面将k维空间切分。    假设数据集\(T\)的大小是\(m*n\),即\(T{x_1,x_2,...x_m}\),其中…

力软 java主从表保存_JAVA常用知识总结(十二)——数据库(二)

MySQL主从热备份工作原理简单的说&#xff1a;就是主服务器上执行过的sql语句会保存在binLog里面&#xff0c;别的从服务器把他同步过来&#xff0c;然后重复执行一遍&#xff0c;那么它们就能一直同步啦。整体上来说&#xff0c;复制有3个步骤&#xff1a;作为主服务器的Maste…

Java——线程的创建,线程池

线程 多线程就是一个程序中有多个线程在同时执行。 多线程下CPU的工作原理 实际上&#xff0c;CPU(中央处理器)使用抢占式调度模式在多个线程间进行着高速的切换。对于CPU的一个核而言&#xff0c;某个时刻&#xff0c;只能执行一个线程&#xff0c;而CPU的在多个线程间切换速度…

java并查集找朋友圈_图—并查集(解决朋友圈问题)

图也是一种 非线性结构&#xff0c;是由多个顶点组成的关系集合组成的一种数据结构。图可以分为两种&#xff0c;无向图和有向图。★图的定义:★典型问题&#xff1a;利用图能够解决很多问题&#xff0c;这里有一个较为典型的问题&#xff0c;假如已知有n个人和m对好友关系(存于…

(三)SpringBoot之配置文件详解:Properties和YAML

一、配置文件的生效顺序&#xff0c;会对值进行覆盖&#xff1a; 1. TestPropertySource 注解2. 命令行参数3. Java系统属性&#xff08;System.getProperties()&#xff09;4. 操作系统环境变量5. 只有在random.*里包含的属性会产生一个RandomValuePropertySource6. 在打包的j…

fscanf()php,fscanf函数的用法

以前解析有规律的文件的时候要么用正则表达式&#xff0c;要么就是傻傻的自己写程序来解析有规律的文件。今天突然发现c的库函数中有一个现成的可以解析有规律的文件的函数&#xff0c;就是fscanf()函数。fscanf 位于头文件中&#xff0c;函数原型为 int fscanf(FILE * stream,…

为什么设计师应该学习编写代码

通常&#xff0c;在完成了一件网页设计后&#xff0c;设计师的无知都会显露无遗而备受指责。他们把创建网页代码的繁重工作都留给了程序员们。这种现象不只出现在网络开发行业&#xff0c;在软件及游戏开发业也是如此&#xff08;完整图文版&#xff09;。残酷的事实就是&#…

iOS内存区域部分内容

目前参考这里&#xff1a; https://www.zhihu.com/question/263823072/answer/273452932 以后整理相关的代码问题。 更多参考资料&#xff1a; https://stackoverflow.com/questions/79923/what-and-where-are-the-stack-and-heap 堆栈&#xff1a;https://baike.baidu.com/ite…