使用Spark Streaming处理爬取的数据(问题总结)

本文总结了在使用Java爬取数据并利用Spark Streaming处理后存入MySQL数据库过程中遇到的问题。包括并发时数据库连接耗尽、数据处理延迟、static变量控制困难、数据库策略不当及线程管理不足等。提出改进方案,如通过Kafka缓冲数据,控制static变量,优化数据库存取策略,并强调了现有方案存在的数据丢失和不准确的风险。后续将针对并发和数据存储的错误进行修复和完善。

一 .背景
       使用java爬取所需的数据,使用spark streaming处理数据后,存入数据库(用的mysql,但不推荐,有很大的后遗症),使用web重新展示出来。

二 .代码

     1.原先的想法与实现

  • 数据库连接池的定义(时间自定义)
package org.com.wh;

import org.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

/**
 * Created by root on 8/2/16.
 */
public class ConnectionPoolTitle {
   
   
    private static Log log = LogFactory.getLog(ConnectionPoolTitle.class);
    private static BasicDataSource bs = null;

    public static BasicDataSource getDataSource() throws Exception{
        if(bs == null){
            bs = new BasicDataSource();
            bs.setDriverClassName("com.mysql.jdbc.Driver");
            bs.setUrl("jdbc:mysql://master:3306/spark");
            bs.setUsername("xxxxx");
            bs.setPassword("xxxxx");
            bs.setMaxActive(500);//设置数据库最大并发数
            bs.setInitialSize(50);//数据库初始化时建立的连接数
            bs.setMinIdle(50);//最小空闲连接数
            bs.setMaxIdle(500);//数据库最大连接数
            bs.setMaxWait(1000);
            bs.setMinEvictableIdleTimeMillis(6);//空闲连接6ms后释放
            bs.setTimeBetweenEvictionRunsMillis(60*1000);//检测是否有死掉的线程
            bs.setTestOnBorrow(true);
        }
        return bs;
    }

    public static void shutDownDataSource() throws Exception{
        if(bs!=null){
            bs.close();
        }
    }

    public static Connection getConnection(){
        Connection connection = null;
        try {
            if (bs != null) {
                connection = bs.getConnection();
            } else {
                connection = getDataSource().getConnection();
            }
        }catch(SQLException e){
            log.error(e.getMessage(),e);
        }catch(Exception e){
            log.error(e.getMessage(),e);
        }
        return connection;
    }

    public static void closeConnection(ResultSet rs, Statement ps, Connection con){
        if(rs!=null){
            try {
                rs.close();
            }catch(SQLException e){
                log.error("rs is Exception"+e.getMessage(),e);
            }
        }
        if(ps!=null){
            try {
                ps.close();
            }catch (SQLException e){
                log.error("ps is Exception"+e.getMessage(),e);
            }
        }
        if(con!=null){
            try {
                con.close();
            }catch (SQLException e){
                log.error("con is Exception"+e.getMessage(),e);
            }
        }
    }
}
  • 线程池的定义
package org.com.wh;

import scala.Tuple2;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Created by root on 8/6/16.
 */
public class ThreadPool {
   
   
    private static ExecutorService pool = Executors.newFixedThreadPool(20);//定义一个有界线程池,20个线程
    <
### 爬取结构化票房数据 Scrapy 是一个强大的爬虫框架,而 Selenium 可以处理动态加载的数据。结合二者来爬取结构化票房数据,需要先安装 Scrapy 和 Selenium 库,同时需要下载对应浏览器的驱动(如 ChromeDriver)。以下是示例代码: ```python import scrapy from selenium import webdriver from selenium.webdriver.chrome.service import Service from selenium.webdriver.chrome.options import Options from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC class BoxOfficeSpider(scrapy.Spider): name = 'box_office_spider' start_urls = ['https://example.com/box-office'] # 替换为实际票房数据页面 def __init__(self, *args, **kwargs): super(BoxOfficeSpider, self).__init__(*args, **kwargs) chrome_options = Options() chrome_options.add_argument('--headless') # 无头模式 service = Service('path/to/chromedriver') # 替换为 ChromeDriver 实际路径 self.driver = webdriver.Chrome(service=service, options=chrome_options) def parse(self, response): self.driver.get(response.url) # 等待动态内容加载完成 try: element = WebDriverWait(self.driver, 10).until( EC.presence_of_element_located((By.ID, 'box-office-table')) # 替换为实际表格 ID ) except: self.driver.quit() return # 提取结构化票房数据 table = self.driver.find_element(By.ID, 'box-office-table') rows = table.find_elements(By.TAG_NAME, 'tr') for row in rows: cells = row.find_elements(By.TAG_NAME, 'td') if cells: data = { 'movie_name': cells[0].text, 'box_office': cells[1].text } yield data self.driver.quit() ``` 在上述代码中,首先创建了一个 Scrapy 爬虫类 `BoxOfficeSpider`,在初始化方法中配置了 Selenium 的 Chrome 浏览器驱动,并设置为无头模式。在 `parse` 方法中,使用 Selenium 打开页面,等待动态内容加载完成,然后提取表格中的结构化票房数据并通过 `yield` 语句返回。 ### 采集豆瓣、知乎非结构化影评文本 同样可以使用 Scrapy 结合 Selenium 来采集豆瓣、知乎等平台的非结构化影评文本。以下是示例代码: ```python import scrapy from selenium import webdriver from selenium.webdriver.chrome.service import Service from selenium.webdriver.chrome.options import Options from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC class ReviewSpider(scrapy.Spider): name = 'review_spider' start_urls = ['https://movie.douban.com/subject/xxxx/reviews', 'https://www.zhihu.com/question/xxxx/answers'] # 替换为实际影评页面 def __init__(self, *args, **kwargs): super(ReviewSpider, self).__init__(*args, **kwargs) chrome_options = Options() chrome_options.add_argument('--headless') service = Service('path/to/chromedriver') self.driver = webdriver.Chrome(service=service, options=chrome_options) def parse(self, response): self.driver.get(response.url) try: element = WebDriverWait(self.driver, 10).until( EC.presence_of_element_located((By.CSS_SELECTOR, '.review-content')) # 替换为实际影评内容选择器 ) except: self.driver.quit() return reviews = self.driver.find_elements(By.CSS_SELECTOR, '.review-content') for review in reviews: yield { 'review_text': review.text } self.driver.quit() ``` 在这个示例中,创建了一个 `ReviewSpider` 类,同样在初始化方法中配置了 Selenium 驱动。在 `parse` 方法中,使用 Selenium 打开页面,等待影评内容加载完成,然后提取影评文本并返回。 ### 利用 Spark Streaming 处理实时票房数据 要利用 Spark Streaming 处理实时票房数据,需要先安装 PySpark 库。以下是示例代码: ```python from pyspark import SparkContext from pyspark.streaming import StreamingContext # 创建 SparkContext 和 StreamingContext sc = SparkContext("local[2]", "BoxOfficeStreaming") ssc = StreamingContext(sc, 10) # 每 10 秒处理一次数据 # 模拟从 Kafka 接收实时票房数据 lines = ssc.socketTextStream("localhost", 9999) # 处理实时票房数据 # 假设数据格式为 "movie_name,box_office" box_office_data = lines.map(lambda line: line.split(",")) total_box_office = box_office_data.map(lambda data: float(data[1])).reduce(lambda a, b: a + b) # 打印结果 total_box_office.pprint() # 启动流处理 ssc.start() ssc.awaitTermination() ``` 在上述代码中,首先创建了 `SparkContext` 和 `StreamingContext`,设置每 10 秒处理一次数据。然后模拟从 Kafka 接收实时票房数据,将数据按逗号分割,提取票房数据并进行累加,最后打印结果。 ### 注意事项 在爬取社交媒体数据时,需要遵守平台的“robots 协议”和“数据使用条款”,不要爬取敏感数据或过量爬取。实际应用中建议使用平台提供的 API(比如微博开放平台的“评论接口”) [^3]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值