Scala学习之爬豆瓣电影

简单使用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. 建立Scala工程,并将Jsoup的jar包加入工程

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)
  }
}
时间: 2024-10-26 09:53:08

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

Node.js学习 爬虫下载豆瓣电影top250图片

利用node.js实现爬虫,并且爬取豆瓣电影top250的列表和图片. 1 什么是node.js 简单的说 Node.js 就是运行在服务端的 JavaScript.Node.js 是一个基于Chrome JavaScript 运行时建立的一个平台.Node.js是一个事件驱动I/O服务端JavaScript环境,基于Google的V8引擎 2 什么是爬虫 (又被称为网页蜘蛛,网络机器人,在FOAF社区中间,更经常的称为网页追逐者),是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本. 3

pyspider爬豆瓣电影实例

直接copy官网实例会出现599的错误,百度了很久发现是因为证书的问题 添加这一句忽略证书 validate_cert = False 代码如下: +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ #!/usr/bin/env python# -*- encoding: utf-8 -*-# Created on 2016-05-21 20:21:32# Project: tutorial_douban_moive f

爬豆瓣电影名

import urllib.request from bs4 import BeautifulSoup url = "https://movie.douban.com/chart" req = urllib.request.Request(url) req.add_header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:54.0) Gecko/20100101 Firefox/5

Python爬虫入门 | 爬取豆瓣电影信息

这是一个适用于小白的Python爬虫免费教学课程,只有7节,让零基础的你初步了解爬虫,跟着课程内容能自己爬取资源.看着文章,打开电脑动手实践,平均45分钟就能学完一节,如果你愿意,今天内你就可以迈入爬虫的大门啦~好啦,正式开始我们的第二节课<爬取豆瓣电影信息>吧!啦啦哩啦啦,都看黑板~1. 爬虫原理1.1 爬虫基本原理听了那么多的爬虫,到底什么是爬虫?爬虫又是如何工作的呢?我们先从"爬虫原理"说起.爬虫又称为网页蜘蛛,是一种程序或脚本.但重点在于:它能够按照一定的规则,自动

用requests和etree爬取豆瓣电影评论

写在前面的话 :上一篇文章我们用requests和lxml.etree爬取了豆瓣电影Top250的电影信息,为了能对requests和lxml.etree有更深的理解,下面我们将继续用他们来爬取豆瓣电影的短评 温馨提示 :博主使用的系统为win10,使用的python版本为3.6.5 一.网页分析 首先我们使用chrome浏览器打开某一部电影的评论(这里示例为最近很火的<一出好戏>),我们首先可以判断该网站是一个静态网页,和之前一样我们可以通过构造URL来获取全部网页的内容,但是这次我们尝试使

团队-爬取豆瓣电影TOP250-需求分析

需求:爬取豆瓣电影TOP250 *向用户展示电影的排名,分数,名字,简介,导演,演员,前10条影评信息,链接信息 实现思路: 分析豆瓣电影TOP250的url规则, 编写模块获取相关url 获取全部相关页面的html代码 分析html中有关"排名,分数,名字,简介,导演,演员,前10条影评信息,链接信息"的标签 结合第三方工具包实现信息的提取 编写将"搜集信息"展示出来的模块

团队-Python 爬取豆瓣电影top250-需求分析

需求:爬取豆瓣电影TOP250 *向用户展示电影的排名,分数,名字,简介,导演,演员,前10条影评信息,链接信息 实现思路: 分析豆瓣电影TOP250的url规则, 编写模块获取相关url 获取全部相关页面的html代码 分析html中有关"排名,分数,名字,简介,导演,演员,前10条影评信息,链接信息"的标签 结合第三方工具包实现信息的提取 编写将"搜集信息"展示出来的模块

《团队-爬取豆瓣电影TOP250-成员简介及分工》

团队项目<爬取豆瓣电影TOP250>开发人员之一:张晓亮,性别男,15级软件技术专业学生,喜欢爱书和浏览网页信息.掌握的知识:Java语言开发,熟悉Python,C等开发语言.了解HTML5,CSS3,Javasprict等前端开发知识.可以熟练使用eclipse,Sequel Pro等软件开发工具. 在本次开发中担任:系统分析.对系统功能需求分析.用户体验设计进行归拢.对系统进度的控制,风险评估进有把控和人员的配置等

Scrapy系列之爬取豆瓣电影

每日一练,每日一博. Scrapy,Python开发的一个快速,高层次的屏幕抓取和web抓取框架,用于抓取web站点并从页面中提取结构化的数据.Scrapy用途广泛,可以用于数据挖掘.监测和自动化测试. 1.确定目标网站:豆瓣电影 http://movie.douban.com/top250 2.创建Scrapy项目: scrapy startproject doubanmovie 3.配置settings.py文件 BOT_NAME = 'doubanmovie' SPIDER_MODULES