我爱编程Python爬虫

二十二.多进程实战 - 简书网热评文章

2018-02-22  本文已影响0人  橄榄的世界

爬取网址:https://www.jianshu.com/c/bDHhpK
爬取信息:用户ID,发表时间,标题,内容,浏览量,评论数,点赞数,打赏数(可能没有)
爬取方式:使用lxml解析。
存储方式:MongoDB存储 & MySQL存储

image.png

1.MongoDB存储代码如下:

import requests
from lxml import etree
import pymongo
from multiprocessing import Pool

#下述三条语句不能放在"if __name__ == '__main__'"中,否则无法写入数据库。
client = pymongo.MongoClient('localhost', 27017) #连接数据库
mydb = client['mydb']
jianshu = mydb['jianshu']   ##创建数据库和数据集合

def get_info(url):
    headers = {'user-agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3294.6 Safari/537.36'}
    r = requests.get(url,headers =headers)
    html = etree.HTML(r.text)
    infos = html.xpath('//ul[@class="note-list"]/li')
    for info in infos:       
        try:
            author = info.xpath('div/div[1]/div/a/text()')[0]
            write_time = info.xpath('div/div[1]/div/span/@data-shared-at')[0]
            title = info.xpath('div/a/text()')[0]
            content = info.xpath('div/p/text()')[0].strip()
            view = info.xpath('div/div[2]/a/text()')[1].strip()
            comment = info.xpath('div/div[2]/a[2]/text()')[1].strip()
            great = info.xpath('div/div[2]/span[1]/text()')[0].strip()
            reward1 = info.xpath('div/div[2]/span[2]/text()')
            if len(reward1)== 0:
                reward = "无"
            else:
                reward = reward1[0].strip()
                
            data = {
                '用户ID':author,
                '发表日期':write_time,
                '标题':title,
                '内容':content,
                '浏览量':view,
                '评论数':comment,
                '点赞数':great,
                '打赏数':reward
            }
            jianshu.insert_one(data)
            
        except IndexError:
            pass 
            
if __name__ == '__main__':
    url_list = ["https://www.jianshu.com/c/bDHhpK?order_by=added_at&page={}".format(str(i)) for i in range(1,1001)]       
    pool = Pool(processes=4)     #创建进程池
    pool.map(get_info, url_list)  #调用进程池

在代码运行过程中,如果想查询当前获取的数据量,可以通过Robomongo,对集合点击右键选择“Statistics”来实现。


image.png

在数值count中即可看到当前获取的数据量。


image.png

当然,通过命令行也可以实现。
① 在bin文件夹中输入mongo
② 输入use mydb
③ 输入db.jianshu.find().count()

image.png
显示结果均为1995。

2.采用MySQL存储
1)先建立数据表 jianshu

  CREATE TABLE jianshu(
  用户ID TEXT,
  发表日期 TEXT,
  标题 TEXT,
  内容 TEXT,
  浏览量 TEXT,
  评论数 TEXT,
  点赞数 TEXT,
  打赏数 TEXT
  )ENGINE INNODB DEFAULT CHARSET=utf8;

2)然后修改了一下导入的模块,写入mysql的数据格式,存储到mysql的操作命令等,基本就可以运作了。需要注意的是,采用多线程池的操作时,不要把mysql的相关操作命令写入"if name == 'main'"中,否则报错。
代码如下:

import requests
from lxml import etree
import pymysql
from multiprocessing import Pool

###下述两条语句不能放在"if __name__ == '__main__'"中,否则无法连接数据库。
conn = pymysql.connect(host='localhost',user='root',db='mydb',port=3306,charset="utf8")  #连接数据库
cursor = conn.cursor()  #光标对象

def get_info(url):
    headers = {'user-agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3294.6 Safari/537.36'}
    r = requests.get(url,headers =headers)
    html = etree.HTML(r.text)
    infos = html.xpath('//ul[@class="note-list"]/li')
    for info in infos:       
        try:
            author = info.xpath('div/div[1]/div/a/text()')[0]
            write_time = info.xpath('div/div[1]/div/span/@data-shared-at')[0]
            title = info.xpath('div/a/text()')[0]
            content = info.xpath('div/p/text()')[0].strip()
            view = info.xpath('div/div[2]/a/text()')[1].strip()
            comment = info.xpath('div/div[2]/a[2]/text()')[1].strip()
            great = info.xpath('div/div[2]/span[1]/text()')[0].strip()
            reward1 = info.xpath('div/div[2]/span[2]/text()')
            if len(reward1)== 0:
                reward = "无"
            else:
                reward = reward1[0].strip()
            cursor.execute('INSERT INTO jianshu(用户ID,发表日期,标题,内容,浏览量,评论数,点赞数,打赏数) VALUES (%s,%s,%s,%s,%s,%s,%s,%s)',
                  (author,write_time,title,content,view,comment,great,reward))   #插入数据
            conn.commit()  #提交事务   **此语句也不能放入"if __name__ == '__main__'"中,否则无法写入数据库。
            
        except IndexError:
            pass 
            
if __name__ == '__main__':    
    url_list = ["https://www.jianshu.com/c/bDHhpK?order_by=added_at&page={}".format(str(i)) for i in range(1,1001)]       
    pool = Pool(processes=4)     #创建进程池
    pool.map(get_info, url_list)  #调用进程池

好不容易让数据入库Mysql,但是运行完几页后,还是出现了点编码方面的小问题:

Traceback (most recent call last):
  File "F:\Python-Ex\try.py", line 588, in <module>
    pool.map(get_info, url_list)  #调用进程池
  File "C:\Python36\lib\multiprocessing\pool.py", line 266, in map
    return self._map_async(func, iterable, mapstar, chunksize).get()
  File "C:\Python36\lib\multiprocessing\pool.py", line 644, in get
    raise self._value
pymysql.err.InternalError: (1366, "Incorrect string value: '\\xF0\\x9F\\x8E\\xA4\\xE5\\xB8...' for column '标题' at row 1")

原因是Mysql里UTF8编码最多只能支持3个字节,而Emoji表情字符使用的UTF8编码,很多都是4个字节,有些甚至是6个字节。
而最简单的方法就是把特殊字符替换掉。
由于时间关系,这里就不展开了。
相关解决方案,可以参考以下链接:
http://blog.csdn.net/z69183787/article/details/48465729

上一篇 下一篇

猜你喜欢

热点阅读