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

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

1、爬虫前期准备

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

  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。

//用于记录总数。和失败次数
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);
}
}

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).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



本文来自伊豚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.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} /**
* 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)
}
}

Scala学习之爬豆瓣电影的更多相关文章

  1. python简单爬豆瓣电影排名

    爬豆瓣电影 网站分析: 1 打开https://movie.douban.com,选择  [排行榜],然后随便选择一类型,我这里选择科幻    2 一直浏览网页,发现没有下一的标签,是下滑再加载的,可 ...

  2. Scrapy 学习笔记爬豆瓣 250

    Scrapy 是比较上层的库,基于中间层开发,它基于高层,所以它依赖许多其它库.事件驱动的异步技术. Scrapy 爬取网页,以豆瓣电影 Top 250 为例子. 首先打开命令提示符,输入.scrap ...

  3. 2_爬豆瓣电影_ajax动态加载

    爬豆瓣 什么是 AJAX ? AJAX 是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术. AJAX = Asynchronous JavaScript and XML(AJAX = 异步 ...

  4. Scrapy爬豆瓣电影Top250并存入MySQL数据库

    d:进入D盘 scrapy startproject douban创建豆瓣项目 cd douban进入项目 scrapy genspider douban_spider movie.douban.co ...

  5. pyspider爬豆瓣电影实例

    直接copy官网实例会出现599的错误,百度了很久发现是因为证书的问题 添加这一句忽略证书 validate_cert = False 代码如下: ++++++++++++++++++++++++++ ...

  6. python爬虫--用xpath爬豆瓣电影

    步骤 将目标网站下的页面抓取下来 将抓取下来的数据根据一定规则进行提取   具体流程 将目标网站下的页面抓取下来 1. 倒库 import requests 2.头信息(有时候可不写) headers ...

  7. [151116 记录] 使用Python3.5爬取豆瓣电影Top250

    这一段时间,一直在折腾Python爬虫.已有的文件记录显示,折腾爬虫大概个把月了吧.但是断断续续,一会儿鼓捣python.一会学习sql儿.一会调试OpenCV,结果什么都没学好.前几天,终于耐下心来 ...

  8. 用Scrapy爬虫下载图片(豆瓣电影图片)

    用Scrapy爬虫的安装和入门教程,这里有,这篇链接的博客也是我这篇博客的基础. 其实我完全可以直接在上面那篇博客中的代码中直接加入我要下载图片的部分代码的,但是由于上述博客中的代码已运行,已爬到快九 ...

  9. 一起学爬虫——通过爬取豆瓣电影top250学习requests库的使用

    学习一门技术最快的方式是做项目,在做项目的过程中对相关的技术查漏补缺. 本文通过爬取豆瓣top250电影学习python requests的使用. 1.准备工作 在pycharm中安装request库 ...

随机推荐

  1. BZOJ 2115 DFS+高斯消元

    思路: 先搞出来所有的环的抑或值 随便求一条1~n的路径异或和 gauss消元找异或和最大 贪心取max即可 //By SiriusRen #include <cstdio> #inclu ...

  2. sql索引碎片产生的原理 解决碎片的办法(sql碎片整理)

    本文讲述了SQL SERVER中碎片产生的原理,内部碎片和外部碎片的概念.以及解决碎片的办法和填充因子.在数据库中,往往每一个对于某一方面性能增加的功能也会伴随着另一方面性能的减弱.系统的学习数据库知 ...

  3. HDU 5214 Movie【贪心】

    题意:给出n,l[1],r[1],a,b,c,d 其中 l[i]=l[i-1]*a+b,r[i]=r[i-1]*c+d; 问能否从这n个区间中选出不相交不重叠的三个区间 看的题解 先考虑最左边的区间, ...

  4. fill,fill-n,memset的区别

    这里在网上搜集归纳了一个总结 memset函数 按照字节填充某字符 在头文件<string.h>中 因为memset函数按照字节填充,所以一般memset只能用来填充char型数组,(因为 ...

  5. maven+spring-data-jpa环境搭建

    转自http://www.cnblogs.com/007sx/p/5658194.html 首先看一下项目结构: 所用到的jar(pom.xml): <project xmlns="h ...

  6. 【问题:SSH】win10使用SSH链接服务器时,提示:Host key verification failed

    异常原因:当前连接新建的验证信息与之前保存的验证信息不一致,将原来的验证信息删除就可以了. 1 使用以下命令,清空之前缓存的信息.或者直接打开C:\Users\Nolan\.ssh\known_hos ...

  7. Object-C,循环语句for,while,do-while

    演示循环语句,for,while,do-while,和C系列的语言,语法基本一致1到10,求和 // // main.m // for-while // // Created by fansunion ...

  8. UVA10269 Adventure of Super Mario(Floyd+DP)

    UVA10269 Adventure of Super Mario(Floyd+DP) After rescuing the beautiful princess, Super Mario needs ...

  9. 洛谷 P1824 进击的奶牛

    P1824 进击的奶牛 题目描述 Farmer John建造了一个有N(2<=N<=100,000)个隔间的牛棚,这些隔间分布在一条直线上,坐标是x1,...,xN (0<=xi&l ...

  10. MongoDB 基本使用

    数据库基本操作 连接到mongoDBserver  1 ./bin/mongo 127.0.0.1:12345  查看当前数据库 1 2 3 > show dbs admin  (empty) ...