week2_实战作业

设计思路

1.从host页面获取赶集网所有城市的二手市场marketurl
2.根据marketurl获取每个板块的blockurl
3.通过分析各板块页脚页码元素,分析每个板块有多少个列表页,得到listurl
ps:由于赶集网列表页设置非常混乱(如不显示页数控件,只有一页却可以访问任意页码等等),此功能并未实现,程序只抓取了每个板块的第一页,即blockurl=listurl
4.分析出listurl中的所有详情页detailurl
5.解析出详情页面中的信息,区分转转和赶集商品,获取商品信息
ps:所有信息均入mongodb表中

代码

main.py

#coding=utf-8
'''爬取赶集网所有城市二手市场所有类目的商品信息,由于赶集网列表页数比较混乱,仅抓取各板块下第一页
爬取类目下所有帖子,信息包括:商品标题,发帖时间, 类型,价格,交易地点,新旧程度等
多进程方式爬取
'''
from configparser import ConfigParser
from pymongo import MongoClient
from ganji_crawler import get_citys_market,get_block_urls,get_detail_page_urls,getinfo_from_detailpage

cf = ConfigParser()  # 创建conf文件解析对象
cf.read("ganji.conf")  # 读取conf文件

host = cf.get('mongodb', 'db_host')
port = cf.getint('mongodb', 'db_port')
client = MongoClient(host, port)  # 连接mongodb,创建客户端
ganji_market = client[cf.get('databases', 'db_market')]  # 连接二手市场数据库

# 创建collections实例
citys_market = ganji_market[cf.get('collections', 'collection_citys')]  # 存放城市url表
city_market_block_url = ganji_market[cf.get('collections', 'collection_block_url')]  # 各城市板块url表
market_detailpage_url = ganji_market[cf.get('collections', 'collection_detail_url')]  # 详情页url
market_goods_infos = ganji_market[cf.get('collections', 'collection_goods_info')]

#############代码执行部分##############
get_citys_market(host,citys_market)   #入库城市及二手市场url

for city in citys_market.find():
    get_block_urls(city['link'],city_market_block_url)   #入库板块url

for block in city_market_block_url.find({}, {'link': 1, '_id': 0}):
    get_detail_page_urls(block['link'],market_detailpage_url) #入库详情页url


for idx, detail in enumerate(market_detailpage_url.find({}, {'link': 1, '_id': 0})):  # 入库商品信息
    getinfo_from_detailpage(detail['link'], market_goods_infos)
    if idx % 1000 == 0:
        print('{} records has been inserted ! '.format(idx))

crawler.py

#coding=utf-8
'''爬取赶集网二手市场数据'''
from bs4 import BeautifulSoup
from pymongo import MongoClient,errors
import requests,re,time

def get_citys_market(host,collection):

    resp = requests.get(host)
    soup = BeautifulSoup(resp.content,'lxml')
    links = soup.select('div.all-city > dl > dd > a ') #获取城市列表中所有超链接
    for link in links:
        collection.insert_one({
            'link' : link['href']+'wu/',
            'city' : link.string
        })

def get_block_urls(city_market_url,collection):
    '''从一个城市的二手市场页面抓取所有区块url
    city_market_url:某个城市的二手市场url
    collection:解析出的板块url存入的数据表'''

    resp = requests.get(city_market_url)
    soup = BeautifulSoup(resp.content,'lxml')
    try:
        div_navigate = soup.select('div.main')[0]
    except IndexError:
        return
    for a in div_navigate.select('a'):
        try:
            href = a['href']
            if href.startswith('/'):    #清洗脏数据,全部分类中有#开头的
                collection.insert_one({'link': city_market_url[:-4] + href})   #拼拼凑板块url,这里因为赶集网设置比较特殊,需要去除一些无效字符
        except errors.DuplicateKeyError as e:
            print(e)

def get_detail_page_urls(blockurl,collection):
    '''赶集网板块的页数判断和访问就是个坑啊,完全没有判断板块下有多少页的规律,这里先提取各版块第一页的详情页url'''

    resp = myRequestGet(blockurl)
    if not resp:
        return

    soup = BeautifulSoup(resp.content,'lxml')
    try:
        layoutlist = soup.select('dl.list-bigpic.clearfix') #定位到每条数据dl标签上
    except IndexError:
        return
    time.sleep(1)
    for layout in layoutlist:
        links = layout.select('a')  #获取此标签下所有超链
        for link in links:
            href = link['href']
            if href.startswith('http://m.zhuanzhuan.58.com'):       #筛选出转转数据url
                #由于获取的转转url是通过js获取商品信息的,所以需要改一下url形式,以便css path可以找到目标信息
                infoId = re.findall(r'infoId=(\d+)&', href)[0]
                href = 'http://zhuanzhuan.58.com/detail/{}z.shtml'.format(infoId)
                try:
                    collection.insert_one({'source':'zhuanzhuan','link': href}) #入库转转url-改写后
                except errors.DuplicateKeyError as e:
                    print(e)
            elif href.endswith('.htm'):
                try:
                    collection.insert_one({'source':'ganji','link': href})  #入库赶集url
                except errors.DuplicateKeyError as e:
                    print(e)

def getinfo_from_detailpage(detailurl,collection):
    '''从详情页获取商品信息'''
    resp = myRequestGet(detailurl)
    if not resp:
        return
    soup = BeautifulSoup(resp.content, 'lxml')
    time.sleep(0.1)

    #转转商品信息获取
    if resp.url.startswith('http://zhuanzhuan'):
        try:
            title = soup.select(' h1.info_titile ')[0].string
            price = ''.join(soup.select(' span.price_now ')[0].stripped_strings)
            area = soup.select(' div.palce_li > span > i ')[0].string
            desc = soup.select(' div.baby_kuang.clearfix > p')[0].string
        except IndexError:
            return
        #入表
        collection.insert_one({
            'source' : 'zhuanzhuan',
            'title': title,
            'price': price,
            'area': area,
            'desc': desc})
    else:   #赶集商品信息获取
        try:
            title = soup.select(' h1.title-name ')[0].string
            price = soup.select(' i.f22.fc-orange.f-type ')[0].string
            area = ''.join(soup.select(' ul.det-infor > li:nth-of-type(3) ')[0].stripped_strings)
            desc = soup.select(' .second-sum-cont')[0].get_text().strip()
        except IndexError:
            return
        collection.insert_one({
            'source': 'ganji',
            'title': title,
            'price': price,
            'area': area,
            'desc': desc
            })

def myRequestGet(url):
    '''会遇到被封的情况,在这里把requests.get包裹一层函数,如果异常,则sleep(10)'''
    try:
        resp = requests.get(url)
        return resp
    except requests.exceptions.RequestException as e:
        print('Requests Error -----------{}-----------wait 10 seconds'.format(str(e.__class__)))
        time.sleep(10)
        return None
    except Exception as e:
        print('Other Eroor -----------{}-----------wait 10 seconds'.format(str(e.__class__)))
        time.sleep(10)
        return None


#判断页面是否存在,由于判断时需要读取页面并生成soup后判断,所以干脆传入soup对象,而不是url
def exists(soup):
    if soup.title.string == '您访问的网页不存在':
        return False
    else:
        return True


if  __name__ == '__main__':

    ##############一些初始化##############
    client = MongoClient('mongodb://localhost:27017')
    ganji_market = client['ganji_market']    #赶集网二手市场数据库
    host = 'http://www.ganji.com/index.htm'

    #创建存储城市及其二手市场url的表
    citys_market = ganji_market['citys_market']

    #创建存储各城市二手市场板块url的collections,并将link字段设置为唯一索引,避免出现重复的link
    city_market_block_url = ganji_market['city_market_block_url']
    city_market_block_url.ensure_index('link', unique=True)


    #创建详情页url存储表
    market_detailpage_url = ganji_market['market_detailpage_url']
    market_detailpage_url.ensure_index('link', unique=True)

    #商品信息入库
    market_goods_infos = ganji_market['market_goods_infos']

    ##############代码执行部分##############
    # get_citys_market(host,citys_market)   #入库城市及二手市场url
    # get_citys_market(host)

    # for city in citys_market.find():
    #     get_block_urls(city['link'],city_market_block_url)   #入库板块url
    # get_block_urls('http://xa.ganji.com/wu/')

    # for block in city_market_block_url.find({}, {'link': 1, '_id': 0}):
    #     get_detail_page_urls(block['link'],market_detailpage_url) #入库详情页url
    # get_detail_page_urls('http://xa.ganji.com/ershoubijibendiannao/')


    for idx,detail in enumerate(market_detailpage_url.find({}, {'link': 1, '_id': 0}).skip(12000)):  #入库商品信息
        getinfo_from_detailpage(detail['link'],market_goods_infos)
        if idx % 1000 == 0:
            print ('{} records has been inserted ! '.format(idx))

    # getinfo_from_detailpage('http://zhuanzhuan.58.com/detail/755842657703362564z.shtml')
    # getinfo_from_detailpage('http://xa.ganji.com/ershoubijibendiannao/2266604261x.htm')

ganji.conf

[mongodb]
db_host = localhost
db_port = 27017

[databases]
db_market = ganji_market

[collections]
collection_citys = citys_market
collection_block_url =  city_market_block_url
collection_detail_url = market_detailpage_url
collection_goods_info = market_goods_infos

总结

1.没有使用多进程,是因为函数都要传入两个参数(包括一个collection对象),暂且先不修改函数适用map方式了。multiprocessing和threading模块还需要再学习一下
2.为了增强程序健壮性,执行过程中增加了一些try模块,在遇到爬虫被封的时候停顿10s再接着抓取,目前看效果不错
3.抓取是按照模板设计的,通用性的问题依然存在,不知是否可以使用scrapy解决
4.引入了一个configparser模块来创建配置文件读取相关信息,以后可以继续使用
5.由于爬虫被封,商品详情url仅抓取了20w,因此商品信息也仅能解析出这么多。后增加了程序的健壮性,未重新运行。

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

推荐阅读更多精彩内容

  • 结果: 作业项目地址 总结 如何判断是否是最后一页 两种实现思路 1,是否有分页符 如果没有,pass 2,是否元...
    超net阅读 3,939评论 1 4
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,128评论 25 707
  • 1 前言 作为一名合格的数据分析师,其完整的技术知识体系必须贯穿数据获取、数据存储、数据提取、数据分析、数据挖掘、...
    whenif阅读 18,072评论 45 523
  • 秋雨绵绵身是客,前路茫茫未可知, 年少轻狂比天高,却如蜉蚁命漂泊, 自古英雄多磨难,无路草莽奔瓦岗, 绿林自有绿林...
    云水解心阅读 449评论 1 7
  • 前些天拜读了村上春树的作品《眠》 颇有同感 不知道有多少人饱受失眠的折磨 有多少个夜里辗转反侧 夜不能寐 不管是学...
    不识故阅读 279评论 2 3