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

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

  1. 爬取1级商品链接

  2. 爬取2级详情信息

  3. 爬取商品详情页

  4. 多进程爬取数据

一、爬取1级商品链接

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

Python学习笔记7——爬取大规模数据_第1张图片
大类链接.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)
Python学习笔记7——爬取大规模数据_第2张图片
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)
Python学习笔记7——爬取大规模数据_第3张图片
解析网页

③爬取大类链接
检查需要爬取的网页,定位链接位置,利用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'))
Python学习笔记7——爬取大规模数据_第4张图片
爬取大类链接

二、爬取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'))
Python学习笔记7——爬取大规模数据_第5张图片
爬取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()
Python学习笔记7——爬取大规模数据_第6张图片
对代码进行整理

三、爬取详情页链接
随意点击进入一个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)
Python学习笔记7——爬取大规模数据_第7张图片
获取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)
Python学习笔记7——爬取大规模数据_第8张图片
爬取详情页链接.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/')
Python学习笔记7——爬取大规模数据_第9张图片
将详情页链接存入数据库

⑤整理完善代码

利用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/')

Python学习笔记7——爬取大规模数据_第10张图片
完善代码

⑥爬取详情页信息并存储
爬取详情页中标题、价格、地区和浏览量的相关信息,并存入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)
Python学习笔记7——爬取大规模数据_第11张图片
商品信息存入数据库

需要注意的是,在构建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()#主进程阻塞,等待子进程的退出
Python学习笔记7——爬取大规模数据_第12张图片
多进程爬取数据

大功告成,之后就是漫长的爬取过程了。还需要提醒的是,在运行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爬虫:四周实现爬虫实战》课程,欢迎感兴趣的朋友购买学习。


Python学习笔记7——爬取大规模数据_第13张图片

你可能感兴趣的:(Python学习笔记7——爬取大规模数据)