Python学习笔记7——爬取大规模数据

我们在爬取数据时,往往是连续爬取上百个页面,本篇以爬取赶集网为例,爬取大规模的数据。步骤如下:

  1. 爬取1级商品链接

  2. 爬取2级详情信息

  3. 爬取商品详情页

  4. 多进程爬取数据

一、爬取1级商品链接

新建一个Python文件,名字命名为my_channel_extracing,用于抓取大类商品链接。以抓取赶集网http://bj.ganji.com/wu/上二手商品为例,右侧的各类商品便是我们需要抓取的大类商品。

大类链接.jpg

①request页面
引入requests对http://bj.ganji.com/wu/进行访问,并输出打印结果,查询是否访问成功。
代码如下:

import requests

url='http://bj.ganji.com/wu/'
wb_data = requests.get(url)
print(wb_data.text)
requests页面

②解析页面
引入BeautifulSoup对网页进行解析,打印输出结果,查看网页解析是否成功。
代码如下:

import requests
from bs4 import BeautifulSoup

url='http://bj.ganji.com/wu/'
wb_data = requests.get(url)
soup = BeautifulSoup(wb_data.text.'lxml')
print(soup)
解析网页

③爬取大类链接
检查需要爬取的网页,定位链接位置,利用for循环输出所爬取的链接。通过观察网页,能发现网页的绝对路劲为http://bj.ganji.com,定义网页绝对路劲,最终输出完整的爬取页面。

import requests
from bs4 import BeautifulSoup

host_url = 'http://bj.ganji.com'#定义网页绝对路径
url = 'http://bj.ganji.com/wu/'
wb_data = requests.get(url)
soup = BeautifulSoup(wb_data.text,'lxml')
links = soup.select('#wrapper > div.content > div > div > dl > dt > a')
for link in links:
    print(host_url+i.get('href'))
爬取大类链接

二、爬取2级商品链接
①爬取2级商品链接
将爬取的1级商品链接放入page_link,通过解析page_link,并再次利用for函数,输出2级商品链接。

import requests
from bs4 import BeautifulSoup

host_url = 'http://bj.ganji.com'#定义网页绝对路径
url = 'http://bj.ganji.com/wu/'
wb_data = requests.get(url)
soup = BeautifulSoup(wb_data.text,'lxml')
links = soup.select('#wrapper > div.content > div > div > dl > dt > a')
for link in links:
     page_link = host_url + link.get('href')#1级网页
     wb_data = requests.get(page_link)
     soup = BeautifulSoup(wb_data.text,'lxml')
     type_links = soup.select('#seltion > div > dl > dd > a')
     for type_link in type_links:
         print(host_url + type_link.get('href'))
爬取2级链接

②整理代码
定义一个函数,将之前写的代码进行封装,并在requests访问网页的时候,设置一个timeout。告诉 requests 在经过以 timeout 参数设定的秒数时间之后停止等待响应。例如设置timeout=6,既如果访问网页不成功,6秒钟之后访问下一个网页。
代码如下:

import requests
from bs4 import BeautifulSoup

def get_index_url():
    urls=[]
    host_url = 'http://bj.ganji.com'#定义网页绝对路径
    url = 'http://bj.ganji.com/wu/'
    wb_data = requests.get(url,timeout=6)#如果程序响应时间超过6秒,则进行下一步
    soup = BeautifulSoup(wb_data.text,'lxml')
    links = soup.select('#wrapper > div.content > div > div > dl > dt > a')
    for link in links:
        page_link = host_url + link.get('href')#1级网页
        wb_data = requests.get(page_link,timeout=6)
        soup = BeautifulSoup(wb_data.text,'lxml')
        type_links = soup.select('#seltion > div > dl > dd > a')
        for type_link in type_links:
            print(host_url + type_link.get('href'))#2级网页
            urls.append(host_url + type_link.get('href')) #将获取的2级网页存入之前设置的urls列表中
    return urls
get_index_url()
对代码进行整理

三、爬取详情页链接
随意点击进入一个2级网页链接,以http://bj.ganji.com/shouji/为例。
①新建Python文件
新建一个Python文件用于抓取详情页链接和详情页中需要爬取的内容。新建文件名取名为my_page_parsing。
②爬取2级页面序列链接
通过观察网页,很容易发现2级网页链接的序列变化是由需要爬取的url+o+数字构成的,如爬取的手机2级网页链接,第二页的url链接为http://bj.ganji.com/shouji/o2/,第三页的url链接为http://bj.ganji.com/shouji/o3/,以此类推。因此,可以利用range和format对爬取2级网页链接进行序列构造。
代码如下:

import requests

url = 'http://bj.ganji.com/shouji/'

for page in range(30):
    list_view = '{}o{}'.format(url,page)
    print(list_view)
获取2级网页序列链接.jpg

③爬取详情页链接
解析上一步中爬取到的2级网页序列链接,通过浏览器检查定位,获取详情页的链接,并筛选出【转转】链接。

import requests
from bs4 import BeautifulSoup

url = 'http://bj.ganji.com/shouji/'
for page in range(30):
    list_view = '{}o{}'.format(url,page)#获取详情页
    print(list_view)
    wb_data = requests.get(list_view)
    soup = BeautifulSoup(wb_data.text,'lxml')
    links = soup.select('td.t > a')
    for link in links:
        item_link = link.get('href')
        if 'zhuanzhuan' in item_link:  # 过滤掉推选链接,仅保留转转链接
            print(item_link)
爬取详情页链接.jpg

④将详情页链接存入MongDB数据库
引入MongDB,进一步筛选掉无法访问的页面,将爬取的详情页链接存入数据库,并定义一个函数,将代码进行封装。

import requests
from bs4 import BeautifulSoup
import pymongo
import time

client = pymongo.MongoClient('localhost',27017)
ganji = client['ganji']
sheet1_url_list = ganji['sheet1_url_list']

def get_link_from(url):
    for page in range(30):
        list_view = '{}o{}'.format(url,page)#获取详情页
        wb_data = requests.get(list_view)
        soup = BeautifulSoup(wb_data.text,'lxml')
        time.sleep(3)
        links = soup.select('td.t > a')
        if soup.select('div.noinfotishi'):#筛选无法访问的页面
            return
        else:
            for link in links:
                item_link = link.get('href')
                if 'zhuanzhuan' in item_link:#过滤掉推选页面,仅保留转转页面
                    print(item_link)
                    sheet1_url_list.insert_one({'url':item_link})

get_link_from('http://bj.ganji.com/shouji/')
将详情页链接存入数据库

⑤整理完善代码

利用try-except语句、引入time第三方库,完善当前的代码。通过在get_link_from定义的函数处新增times变量,利用if、try-excepy实现如下功能:当访问页面超出设定次数时,便直接返回。
try-except语句的用法是:

try:
    <语句>        #运行代码
except:
    <异常处理的语句>        #如果在try部份引发了异常,运行异常处理语句

修改之后的代码如下:

import requests
from bs4 import BeautifulSoup
import pymongo
import time

client = pymongo.MongoClient('localhost',27017)
ganji = client['ganji']
sheet1_url_list = ganji['sheet1_url_list']

def get_link_from(url,times=0):
    if times > 10:#当访问次数超过10次仍然无法读取网页时,则直接返回
        return
    for page in range(3):
        list_view = '{}o{}'.format(url,page)#获取详情页
        try:
            wb_data = requests.get(list_view,timeout=6)
        except:
            return get_link_from(url,times+1)#每次访问不成功,times便增加一次
        soup = BeautifulSoup(wb_data.text,'lxml')
        time.sleep(3)
        links = soup.select('td.t > a')
        if soup.select('div.noinfotishi'):#筛选无法访问的页面
            return
        else:
            for link in links:
                item_link = link.get('href')
                if 'zhuanzhuan' in item_link:#过滤掉推选页面,仅保留转转页面
                    print(item_link)
                    sheet1_url_list.insert_one({'url':item_link})

get_link_from('http://bj.ganji.com/shouji/')

完善代码

⑥爬取详情页信息并存储
爬取详情页中标题、价格、地区和浏览量的相关信息,并存入MongDB数据库中。具体的方法和爬取链接相差不大,直接贴代码出来。

import requests
from bs4 import BeautifulSoup
import pymongo
import time

client = pymongo.MongoClient('localhost',27017)
ganji = client['ganji']
sheet1_url_list = ganji['sheet1_url_list']
sheet2_item_info = ganji['sheet2_item_info']

def get_link_from(url,times=0):
    if times > 10:#当访问次数超过10次仍然无法读取网页时,则直接返回
        return
    for page in range(3):
        list_view = '{}o{}'.format(url,page)#获取详情页
        #print(list_view)
        try:
            wb_data = requests.get(list_view,timeout=6)
        except:
            return get_link_from(url,times+1)#每次访问不成功,times便增加一次
        soup = BeautifulSoup(wb_data.text,'lxml')
        time.sleep(3)
        links = soup.select('td.t > a')
        if soup.select('div.noinfotishi'):#筛选无法访问的页面
            return
        else:
            for link in links:
                item_link = link.get('href')
                if 'zhuanzhuan' in item_link:#过滤掉推选页面,仅保留转转页面
                    print(item_link)
                    sheet1_url_list.insert_one({'url':item_link})

def get_item_info_from(url,times=0):
    if times>10:
        return
    try:
        wb_data = requests.get(url,timeout=6)
    except:
        return get_item_info_from(url,times+1)
    soup = BeautifulSoup(wb_data.text, 'lxml')
    title = soup.select('div.info_lubotu.clearfix > div.box_left_top > h1')
    price = soup.select('div.info_lubotu.clearfix > div.info_massege.left > div.price_li > span > i')
    area = soup.select('div.info_lubotu.clearfix > div.info_massege.left > div.palce_li > span > i')
    view = soup.select('body > div.content > div > div.box_left > div.info_lubotu.clearfix > div.box_left_top > p > span.look_time')
    data = {
        'title': title[0].text if title else None,
        'price': price[0].text if price else 0,
        'area': area[0].text if area else None,
        'view': view[0].text if view else None,
        'url':url
    }
    sheet2_item_info.insert_one(data)
    print(data)
商品信息存入数据库

需要注意的是,在构建data字典填入数据时,用到了if-else语句,这个一定要加上,否则在运行时,如果没有找到相关信息,是会报错的。这个语句的意思是,如果能找到爬取信息的第一个元素,则填入元素,若爬取的网页没有我们需要的信息,则填为空。

4、多进程爬取数据

完成上面的工作,我们的主要任务就已经完成了。接下来是利用多进程,爬取我们需要的信息。
什么是多进程多线程呢?多进程和多线程是一种并发技术,就是可以让你在同一时间同时执行多条任务的技术。进程是程序在计算机上的一次执行活动,线程就是把一个进程分为很多片,每一片都可以是一个独立的流程。用侯爵老师课程中的类比来看,可以将电脑比喻为一家餐厅,餐厅内的餐桌数就是电脑CPU的内核数,当CPU的一个内核运行一个程序时,就是单进程单线程。当CPU的一个内核运行多个程序时,就是单进程多线程。当CPU调用多个内核运行一个程序时,就是多进程单线程。当CPU调用多个内核运行多个程序时,就是多进程多线程。

单进程单线程:指一张桌子上一个人吃饭
单进程多线程:指一张桌子多个人吃饭
多进程单线程:指多张桌子,每张桌子上只有一个人吃饭
多进程多线程:指多张桌子,每张桌子上多个人吃饭

本次采用的方法是多进程单线程爬取数据,新建一个Python文件命名为my_main。
代码如下:

from multiprocessing import Pool #引入多进程库,帮助电脑调用CPU的多个内核
from my_page_parsing import get_link_from,get_item_info_from,sheet1_url_list#引入之前定义的函数

if __name__ =="__main__":#避免产生名称混乱。作用为:如果模块是被直接运行的,则代码被运行,如果模块是被导入的,则代码不被运行
    pool = Pool() #创建进程池
    pool.map(get_item_info_from,[i['url'] for i in sheet1_url_list.find()])#将shee1_url_list中的链接依次放入get_item_info_from函数中
    pool.close()#关闭pool,使其不再接受新的任务
    pool.join()#主进程阻塞,等待子进程的退出
多进程爬取数据

大功告成,之后就是漫长的爬取过程了。还需要提醒的是,在运行my_main时,记得将my_page_parsing中,运行函数时使用的特定url取掉,否则就只是爬取指定的url了。

总结:

1.在爬取数据量巨大的网站时,可以将爬取过程分步骤逐个击破,① 爬取1级商品链接,②爬取2级详情信息,③爬取商品详情页,④构造多进程代码爬取数据。
2.为了防止爬取出错,可以利用times、timeout、try-except等方法完善代码,这些细微的调整是需要时间积累的,通过不断地去实践,不断总结其中规律。
3.需要注意的坑:①if __name__ =="__main__":是一个固定的写法,并不会因为Python文件的命名而改变;②爬取详情页中标题、价格、地区和浏览量的相关信息,构建data字典填入数据时,用到了if-else语句,这是为了防止出现空值而报错,一定要加上。


PS:以上的学习笔记来源于侯爵老师的《0基础Python爬虫:四周实现爬虫实战》课程,欢迎感兴趣的朋友购买学习。


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

推荐阅读更多精彩内容