引言
本次爬虫使用了selenium,结合Chrome浏览器进行页面爬取。数据存储用的是MySQL数据库,个人感觉用MongoDB更好一些,但是最近在学MySQL数据库,所以就果断用它了。
思路分析
起始URL:https://www.jianshu.com/
思路比较简单,就是先访问起始URL(简书的主页),再找到文章的URL,打开该URL,爬取想要文章的信息即可。思路虽然简单,但是我却遇到了不少的问题,最大的问题就是,如果你只是打开简书的主页而不滚动滚动条,页面中文章的URL是有限的,也就是说只能爬取页面中文章的URL,要想出现更多的文章必须要滚动滚动条,我的处理方法时,写一条js代码,当页面的URL都爬完后,执行一次js代码,让滚动条到达页面的底部。
window.scrollTo(0,document.body.scrollHeight)
但是,在多次滚动滚动条后,会出现阅读更多的按钮,此时再滚动滚动条就已经没有用了,所以,要找到这个按钮并且模拟点击。
完整代码
# !/usr/bin/env python
# —*— coding: utf-8 —*—
# @Time: 2020/2/5 20:21
# @Author: Martin
# @File: jianshu.py
# @Software:PyCharm
from selenium import webdriver
from lxml import etree
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
import time
import pymysql
class JianshuSpider(object):
driver_path = r'./chromedriver.exe'
# 爬取的文章数目
article_num = 50
# 文章索引
article_index = 1
url_set = set()
def __init__(self):
self.driver = webdriver.Chrome(executable_path=JianshuSpider.driver_path)
self.url = 'https://www.jianshu.com'
self.conn = pymysql.connect(
host='127.0.0.1',
port=3306,
user='root',
password='root',
database='jianshu',
charset='utf8mb4'
)
self.cursor = self.conn.cursor()
def run(self):
js_code = 'window.scrollTo(0,document.body.scrollHeight)'
self.driver.get(self.url)
self.driver.execute_script(js_code)
self.driver.execute_script(js_code)
source = self.driver.page_source
self.parse_page_list(source)
while True:
self.driver.execute_script(js_code)
try:
btn = self.driver.find_element_by_class_name("load-more")
btn.click()
except :
self.driver.execute_script(js_code)
self.driver.execute_script(js_code)
source = self.driver.page_source
self.parse_page_list(source)
if self.article_index > self.article_num:
break
def parse_page_list(self, source):
html = etree.HTML(source)
li_list = html.xpath('//ul[@class="note-list"]/li')
for li in li_list:
url = self.url + li.xpath('./div/a/@href')[0]
if url not in self.url_set:
self.parse_detail_page(url)
self.url_set.add(url)
time.sleep(1)
def parse_detail_page(self, url):
js_ = 'window.open("%s")' % url
self.driver.execute_script(js_)
self.driver.switch_to.window(self.driver.window_handles[1])
WebDriverWait(driver=self.driver, timeout=10).until(
EC.presence_of_element_located((By.XPATH, '//h1[@class="_1RuRku"]'))
)
source = self.driver.page_source
html = etree.HTML(source)
title = html.xpath('//h1[@class="_1RuRku"]/text()')[0]
author = html.xpath('//span[@class="FxYr8x"]/a/text()')[0]
avatar = html.xpath('//div[@class="_2mYfmT"]/a/img/@src')[0]
public_time = html.xpath('//div[@class="s-dsoj"]/time/text()')[0]
p_list = html.xpath('//article[@class="_2rhmJa"]/p')
content = ""
for p in p_list:
text = p.xpath('./text()')
content += "".join(text)
data = [title, author, avatar, public_time, content]
print(self.article_index, data)
self.save(data)
self.article_index += 1
self.driver.close()
self.driver.switch_to.window(self.driver.window_handles[0])
def save(self, data):
sql = 'insert into article(title,author,avatar,public_time, content) values(%s,%s,%s,%s,%s);'
self.cursor.execute(sql, (data[0], data[1], data[2], data[3], data[4]))
self.conn.commit()
def close(self):
self.cursor.close()
self.conn.close()
self.driver.close()
if __name__ == '__main__':
spider = JianshuSpider()
spider.run()
spider.close()
总结分析
爬取页面的效率比较低,有的时候会在一个页面停留很久。在判断一个文章的URL是否已经爬过时,我用的方法是,将爬取过的URL放到一个集合中,当有新的URL时,就判断新的URL是否在这个集合中,如果在集合中,就证明已经爬取过了,如果没在集合中,就爬取该URL,并将其加入集合。不知道有没有更好的方法,如果有的话,烦请大佬们在文章底部评论告诉我,在此我先行谢过了。