python大数据-多线程数据抓取-58同城转转网的二手产品-断点续传

目标

多线程数据抓取-58同城转转网的二手产品

实作

1. 建立一个项目

新建一个项目58tongcheng1

2. 观察页面特征

不同页面的不同规则问题
分页问题

3. 设计工作流程

首先,在主列表页爬取所有商品的URL,存储在mongodb中,在数据库中建立对应的URL_list (爬虫1)
然后,详情页具体产品的信息,存储在数据库item_info中 (爬虫2)

爬虫2应把 爬虫1抓取的并已存在数据库中的URL取出来,依次读取详情页,获得所要信息,再把这些信息存储在item_info这个表单中

4. 创建channel_extract.py获取每个分类产品的链接

from bs4 import BeautifulSoup
import requests

start_url = 'http://bj.58.com/sale.shtml'
url_host = 'http://bj.58.com'

def get_channel_urls(url):
    wb_data = requests.get(start_url)
    soup = BeautifulSoup(wb_data.text, 'lxml')
    links = soup.select('ul.ym-submnu > li > b > a')  # 寻找该标签时比较麻烦,因为它是hover显示
    print(links)
    for link in links:
        page_url = url_host + str(link.get('href'))
        print(page_url)

get_channel_urls(start_url)  # 通过它打印出所有的URL

把所有的URL集中起来建立一个新的长字符串

channel_list = '''
http://bj.58.com/shouji/
http://bj.58.com/tongxunyw/
http://bj.58.com/danche/
http://bj.58.com/diandongche/
http://bj.58.com/fzixingche/
http://bj.58.com/sanlunche/
http://bj.58.com/peijianzhuangbei/

5. 创建page_parsing.py获取产品详情

from bs4 import BeautifulSoup
import requests
import time
import pymongo

client = pymongo.MongoClient('localhost', 27017)
chengxu = client['chengxu']
url_list = chengxu['url_list3']
item_info = url_list['item_info3']

# spider 1 爬取首页中显示的类目中,一个类目下的所有商品的链接
def get_links_from(channel,pages,who_sells=0): # who_sells = 0表示个人,1表示商家
    #http://bj.58.com/shouji/1/pn2/
    list_view = '{}{}/pn{}/'.format(channel,str(who_sells),str(pages)) # 找网页规律的时候,刚刷新和点击后的相同页面的网址会有变化,但页面相同,它们是等价的,所以找页面规律时要多点击或刷新来找
    wb_data = requests.get(list_view)
    time.sleep(1)
    soup = BeautifulSoup(wb_data.text, 'lxml')
    if soup.find('td','t'):  # 一个类目的页码是有限的,通过寻找td.t来判断系统是否爬过头了
        for link in soup.select('td.t  a.t'):  # 这里的td.t a.t 是点击某个分类后的新网页的每个具体商品的链接的selector
        #for link in soup.select( ('td.t a.t') if not soup.find_all('zhiding', 'huishou') else None ): #修改失败,计划排除被抓取的几排广告
            # 注意!!!上面代码后面,若是('td.t >a.t')即无法显示结果,必须空格!这样才对('td.t > a.t')

            item_link = link.get('href').split('?')[0] # 这里的0是对切片后的字符串形成的列表list进行筛选,选第一段,即0(for in 就是对列表的)
            url_list.insert_one({'url': item_link })  # insert是数据库函数,注意区分
            print(item_link)
    else:
        pass
#get_links_from('http://bj.58.com/shuma/', 2)

# spider 2 爬详情页的数据
def get_item_info(url):
    wb_data = requests.get(url)
    soup = BeautifulSoup(wb_data.text, 'lxml')
    no_longer_exist = '商品已下架' in soup.find('div', "button_li").get_text() # 从下方 AAA 处移过来的代码,理解时先忽略它。
    # find()里面的代码实际是完整的div="button_li",而且要保证该段代码在正常网页和已下架网页中都存在,否则正常网页报错。
    if no_longer_exist:
        pass
    else:
        title = soup.title.text
        price = soup.select('span.price_now i')[0].text
        # 后面必须加[0].text,因为数据库要是str才能存进去,soup.select返回的对象是list,就算list里面只有一个元素,也不能用.text方法,所以才选择用[0],把元素从list调出来,再进行.text方法
        area = soup.select('.palce_li i')[0].text if soup.find_all('i') else None
        item_info.insert_one({'title':title, 'price':price, 'area':area })
        print({'title': title, 'price': price, 'area':area})

#get_item_info('http://zhuanzhuan.58.com/detail/919823388320399372z.shtml')

#======= AAA 爬取的商品链接中有失效的,剔除它(商品已交易则该网址会失效),测试完该段代码备注掉==========#
# url = 'http://zhuanzhuan.58.com/detail/922439089107222541z.shtml'  # 网址上的商品已下架
# wb_data = requests.get(url)
# soup = BeautifulSoup(wb_data.text, 'lxml')
#print(soup.prettify())

# 上面的步骤查询了失效网址的结构。
#no_longer_exist = '商品已下架' in soup.find('span', "soldout_btn").get_text()  # 搬到上方get_item_info
#print (no_longer_exist) # 查看no_longer_exist是True False。上面的find里代码必须是完整的<xxx>内容,形成一个list,否则系统报错属性错误或者无法迭代

注意事项均备注在代码中。。。

6. 多进程数据抓取

建立主程序 main.py

from multiprocessing import Pool #
from channel_extract import channel_list
from page_parsing import get_links_from

def get_all_links_from(channel):
    for num in range(1,51):
        get_links_from(channel,num)

if __name__=='__main__':   # 一种类似作文开头的感谢领导的套话格式,防止上下程序串混乱了,没特别的意思
    pool = Pool()  # 创建进程池
    pool.map(get_all_links_from, channel_list.split())
    # map函数的特点是把括号内的后一个参数放到前一个参数(函数)里去依次执行。约定俗成map第一个参数为不带 () 的函数。
    # channel_list 是引用过来的,我们之前定义过它是一个长字符串,将它分成一段段,split()函数会将一个字符串自动变成分割好的一个大list

监控程序 counts.py

import time
from page_parsing import url_list # url_list 是数据库的第一张表的名称

while True:
    print(url_list.find().count())
    # find()展示url_list中所有的元素,count()计数,这两种函数是数据库函数,不能用于字典和列表
    time.sleep(5)
# 该段程序用来监控用,当它和主程序一起开的时候,它可以计算数量进程,方便管理

7. 运行

打开终端,开启3个窗口,切换到程序文件夹中,第一个窗口输入mongod,输入mongo,好了,mongo已开启
第二个窗口输入 python3 counts.py
第三个窗口输入python3 main.py
好了,开始抓取数据了,成功

8. 断点续传

from page_parsing import get_links_from, get_item_info, url_list, item_info    # 该条为更改的,下面代码全部是新建的

# 断点续传
    db_urls = [ item['url'] for item in url_list.find() ]  # 用列表解析式装入所要爬取的链接
    index_urls = [ item['url'] for item in item_info.find() ]   # 所引出详情信息数据库中所有的现存的 url 字段
    x = set(db_urls)     # 转换成集合的数据结构
    y = set(index_urls)
    rest_of_urls = x - y

设计思路:

  1. 分两个数据库,第一个用于只用于存放抓取下来的 url (ulr_list);第二个则储存 url 对应的物品详情信息(item_info)
  2. 在抓取过程中在第二个数据库中写入数据的同时,新增一个字段(key)'index_url' 即该详情对应的链接
  3. 若抓取中断,在第二个存放详情页信息的数据库中的 url 字段应该是第一个数据库中 url 集合的子集
  4. 两个集合的 url 相减得出圣贤应该抓取的 url 还有哪些


备注

(1) find()的参数依次为(标签名,标签属性),返回一个标签(可多重嵌套)或None;

(2)find_all()的参数依次为(标签名,标签属性),返回一个标签列表或者空列表
(3) python的find()是字符串对象的方法,用于查找子字符串,返回第一个字串出现的位置或-1(字串不存在);mongodb的find()是列表对象的方法,接收字典参数,键值对为所要查找条目键值对,用于查找条目,返回True
(4) mongodb的查询方法find()find_one()
find()方法成功找到符合条件的记录则返回一个生成器(实质是停留在符合条件记录的集合的第一条记录位置的cursor),用list方法转化为列表后,如果该存在符合条件的记录,则生成一个列表,否则生成一个空列表。

find_one({查询键值对},{显示字段:0表示不显示or1表示显示,其余默认不显示,'_id'默认显示})返回查询到的第一条。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容