主要内容:使用selenium和bs4对中文突发事件新闻进行定时自动爬取
代码主体:EventExtractor、ChinaEmergencyNewsExtractor、save_results、contentExtractor

EventExtractor

使用该类控制爬虫获取网页信息和处理网页信息
1.__init__(self)中配置一些默认参数:

  • options.add_experimental_option("prefs", {"profile.managed_default_content_settings.images": 2}):关闭浏览器的图片,提高网页响应效率
  • chrome_options.add_argument('--headless'):隐藏弹出的浏览器
  • webdriver.Chrome():这里使用Google浏览器驱动
  • url:爬取主页url
  • html:存放爬取的html数据
  • save_dir:保存爬取文件目录

    1. # 爬取器
    2. class EventExtractor():
    3. def __init__(self):
    4. options = webdriver.ChromeOptions()
    5. options.add_experimental_option("prefs", {"profile.managed_default_content_settings.images": 2})
    6. chrome_options = Options()
    7. chrome_options.add_argument('--headless')
    8. self.browser = webdriver.Chrome("chromedriver.exe", options=options,chrome_options=chrome_options)
    9. self.url = 'http://www.12379.cn/html/gzaq/fmytplz/index.shtml'
    10. self.html = None
    11. self.save_dir = './chinaEmergency/chinaEEP.xlsx'
    12. # 登录
    13. def login(self):
    14. self.browser.get(self.url)
    15. WebDriverWait(self.browser, 1000).until(
    16. EC.presence_of_element_located((By.CLASS_NAME, 'main'))
    17. )
    18. # 获取当前网页资源
    19. def get_html(self):
    20. self.html = self.browser.page_source
    21. # 跳转下一页
    22. def next_page(self,verbose=False):
    23. try:
    24. submit_page = self.browser.find_element(by=By.XPATH,value=r"//*[@class='next']") # find_element_by_xpath(r"//*[@class='next']")
    25. submit_page.click()
    26. WebDriverWait(self.browser, 1000).until(
    27. EC.presence_of_element_located((By.CLASS_NAME, 'main'))
    28. )
    29. curr_page = self.browser.find_element(by=By.XPATH,value=r"//*[@class='avter']")
    30. if verbose and curr_page != 0:
    31. print(int(curr_page) - 1)
    32. except:
    33. print("页面挑战异常结束")
    34. # 网页处理函数
    35. def ProcessHTML(self, verbose=False, withDuplicates=True):
    36. """
    37. html:html格式的输入
    38. verbose:控制爬取可视化打印的标志位,0表示不显示,1表示html打印,2表示详细打印,默认为1
    39. 函数输入:html
    40. 函数输出:具有新闻信息的pandas表格
    41. """
    42. duplicates_flag = False
    43. if not os.path.exists(self.save_dir):
    44. names = ['title', 'content', 'public_time', 'urls']
    45. history = pd.DataFrame([], columns=names)
    46. history.to_excel(self.save_dir, index=False)
    47. print("首次运行 爬取时间:{}".format(time.strftime('%Y.%m.%d', time.localtime(time.time()))))
    48. else:
    49. history = pd.read_excel(self.save_dir)
    50. history_key = history["public_time"].sort_values(ascending=False).values[0]
    51. print("历史爬取节点:{} 爬取时间:{}".format(history_key,time.strftime('%Y.%m.%d',time.localtime(time.time()))))
    52. soup = BeautifulSoup(self.html)
    53. tbody = soup.find("div", attrs={"class": "list_content"})
    54. for idx, tr in enumerate(tbody.find_all("li")):
    55. # 设置随机睡眠,防止封IP
    56. if random.randint(1, 2) == 2:
    57. time.sleep(random.randint(3, 6))
    58. a = tr.find("a")
    59. title = a.text
    60. href = "http://www.12379.cn" + a["href"]
    61. content = contentExtractor(href)
    62. public_time = tr.find("span").text
    63. results = [title, content, public_time, href]
    64. if verbose:
    65. print([public_time, title, content, href])
    66. # 查重
    67. if withDuplicates and public_time in history["public_time"].values and title in history["title"].values:
    68. duplicates_flag = True
    69. print("监测到重复爬取,已停止后续爬取行为")
    70. break
    71. else:
    72. save_results(results)
    73. # 查重
    74. return duplicates_flag
    75. # 关闭driver
    76. def close(self):
    77. self.browser.close()

    2.login(self):访问网址,until中是等待延时,只有检测到有时才回到下一步
    3.get_html(self):获取当前页面html数据
    4.next_page:页面跳转
    5.ProcessHTML(self, verbose=False, withDuplicates=True):数据处理

  • 首次运行会创建一个新的excel文件,已有历史文件会根据最近发布日期进行去重爬取,当检测到标题和时间都存在则停止爬取。

  • 对每一条信息的爬取设置了sleep时间,以免对服务器造成负担
  • 其他信息爬取比较简单不详细讲解了
  • 对于正文内容的抽取,另外启动一个selenium进行爬取(注意一些链接已经失效,404会直接返回正文为”无”),代码如下:

    1. # 正文抽取函数
    2. def contentExtractor(url):
    3. """
    4. 函数输入:url
    5. 函数输出:输出当前url下的新闻正文内容,若没有新闻内容则输出"无"
    6. """
    7. # 先检测是否404
    8. user_agent = {
    9. 'User-Agent': 'Mozilla/5.0.html (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.html.2171.71 Safari/537.36'}
    10. r = requests.get(url, headers=user_agent, allow_redirects=False)
    11. if r.status_code == 404:
    12. return "无"
    13. else:
    14. # 没有404,则抽取网页正文内容
    15. chrome_options = Options()
    16. chrome_options.add_argument('--headless')
    17. driver = webdriver.Chrome("chromedriver.exe", chrome_options=chrome_options)
    18. driver.get(url)
    19. WebDriverWait(driver, 1000).until(
    20. EC.presence_of_element_located((By.CLASS_NAME, 'content_text'))
    21. )
    22. respond = driver.page_source
    23. driver.quit()
    24. soup = BeautifulSoup(respond)
    25. text = soup.find("div", attrs={"class": "content_text"}).text
    26. return text
  • 如果没有检测到重复则会调用save_results(results)写入当前数据,代码如下:

    1. def save_results(results):
    2. results_path = './chinaEmergency/chinaEEP.xlsx'
    3. names = ['title', 'content', 'public_time', 'urls']
    4. results = {k: v for k, v in zip(names, results)}
    5. if not os.path.exists(results_path):
    6. df1 = pd.DataFrame([], columns=names)
    7. df1.to_excel(results_path, index=False)
    8. else:
    9. df1 = pd.read_excel(results_path)
    10. new = pd.DataFrame(results, index=[1])
    11. df1 = df1.append(new, ignore_index=True)
    12. df1.sort_values(by="public_time", ascending=True, inplace=True)
    13. df1.reset_index(drop=True, inplace=True)
    14. df1.to_excel(results_path, index=False)

    ChinaEmergencyNewsExtractor

    通过该函数完成翻页爬虫
    Event = EventExtractor()创建之前写好的爬取器
    Event.login()登录爬取网站
    通过for循环遍历输入的页码,页码可自有输入
    Event.get_html()获取当前页面HTML数据
    Event.ProcessHTML(verbose=True)处理HTML数据并保存到指定Excel文件中,返回一个是否重复爬取的布尔值。重复则停止翻页,不重复则使用Event.next_page()翻页。
    Event.close()关闭selenium浏览器

    1. # 可控制的事件抽取器
    2. def ChinaEmergencyNewsExtractor(page_nums=3,verbose=1):
    3. Event = EventExtractor()
    4. Event.login()
    5. for i in range(0, page_nums):
    6. Event.get_html()
    7. page_break = Event.ProcessHTML(verbose=True)
    8. if page_break:
    9. break
    10. Event.next_page()
    11. # 关闭driver,防止后台进程残留
    12. Event.close()

    自动爬取和主函数

    自动爬取采用的非常简单的定时爬取策略,采用retry库可以快速完成定时任务,主体代码如下:

    1. # 定时抽取
    2. @retry(tries=30,delay=60*60*24)
    3. def retry_data():
    4. ChinaEmergencyNewsExtractor(page_nums=3, verbose=1)
    5. raise

    使用raise触发异常,让retry库重启函数
    tries:表示重启次数,这里设置了30次
    delay:表示延迟重启的时间,以秒为单位,代码中60*60*24就是延迟一天
    即每个24小时爬取一次网页内容,共计爬取30次,完成一个月的数据自动获取

主函数:

  1. # 主函数
  2. if __name__ == '__main__':
  3. retry_data()

效果展示

image.png
图1 挂机爬取界面

image.png
图2 爬取文件

至此突发事件舆情新闻的自动爬取实例展示完成,数据挖掘与分析萌新,才疏学浅,有错误或者不完善的地方,请批评指正!!