打卡:大作业|爬取一页商品数据

目标:

爬取某8的一页商品信息,介于老师给的网址全是转转信息因此我自己选择了一个卖狗的页面(捂脸)
界面如下:(排除了心宠的所有推广)

打卡:大作业|爬取一页商品数据_第1张图片
网页截图

我们需要爬取商家和个人两个部分的详情页信息

爬取结果:

打卡:大作业|爬取一页商品数据_第2张图片
个人详情页网址
打卡:大作业|爬取一页商品数据_第3张图片
商家详情页网址
打卡:大作业|爬取一页商品数据_第4张图片
个人部分信息
打卡:大作业|爬取一页商品数据_第5张图片
商家部分信息

难点

  • 浏览人数的爬取
  • 爬取的网址跟跳转的网址不同
  • 不同网页完善的信息不同(比如有的有电话有的没有)
  • 个人和商家版的详情页元素位置有轻微区别(比如同一个位置会存放不同内容信息)
  • 爬取的文本有大量/t/r/n的信息
  • 挑战自己爬了下需要点击才能显示的电话号码

代码

from bs4 import BeautifulSoup
import time
import requests


def get_urls(path,name_num):
    respond = requests.get(path)
    soup = BeautifulSoup(respond.text, 'lxml')
    pre_urls = soup.select('''td.t > a[onclick="clickLog('from=pc_cwgou_list_wenzi');"]''')
    i = 0
    urls = []
    file = open('./urls{}.txt'.format(name_num),'a')
    for url in pre_urls:
        i = i + 1
        href = url.get('href')
        urls.append(href)
        text = url.get_text()
        print(i,' ',href,text)
        file.write(str(i)+' '+ href +'\n'+text+'\n\n')
    file.close()
    return urls

def get_views(url):
    time.sleep(2)
    get_num1 = url.split('x.shtml')
    get_num2 = get_num1[0].split('dog/')[-1]
    api = 'http://jst1.58.com/counter?infoid={}'.format(get_num2)
    headers = {
        'Referer':'http://m.58.com/tj/dog/{}'.format(get_num2),
        'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1'
    }
    js = requests.get(api,headers = headers)
    views = js.text.split('total=')[-1]
    return views

def detail (path, count, Hkind_judge) :
    respond = requests.get(path)
    views = get_views(respond.url)
    soup = BeautifulSoup(respond.text,'lxml')
    url_kind = soup.select('#header > div.breadCrumb.f12 > span:nth-of-type(4) > a')
    kind = soup.select('body > div.content_quanbu > div.zhanshi_top.clearfix > div.zhanshi_top_l.fl.clearfix > div.col_sub.sumary.fl > ul > li:nth-of-type({}) > div.su_con > span:nth-of-type(1)'.format(3 if Hkind_judge == 0 else 4))
    title = soup.select('body > div.content_quanbu > div.col_sub.mainTitle > div > h1')
    date = soup.select('#index_show > ul.mtit_con_left.fl.clearfix > li.time.fl > span')
    num = soup.select('span.zhishu')
    pos = soup.select('span.dizhi')
    host = soup.select('span.lianxiren > a')
    phone = soup.select('#t_phone')
    if Hkind_judge == 0:
        host_kind = '个人'
    elif Hkind_judge == 1:
        host_kind = '商家'
    else:
        host_kind = '未知'
    list = [
        '网页分类:'+ url_kind[0].get_text() if url_kind else '网页分类:None',
        '网页标题:'+ title[0].get_text() if title else '网页标题:None',
        '发帖日期:'+ date[0].get_text() if date else '发帖日期:None',
        '店家类型:'+ host_kind,
        '浏览次数:'+ views,
        '宠物种类:'+ kind[0].get_text().replace('\t', '').replace('\n','').replace(' ','') if kind else '宠物种类:None',
        '主人:'+ host[0].get_text() if host else '主人:None',
        '电话:'+ phone[1].get_text() if len(phone) > 1  else '电话:None',
        '只数:'+ num[0].get_text() if num else '只数:None',
        '位置:'+ pos[0].get_text() if pos else '位置:None'
    ]
    print(list)
    file_path = './imformation.txt'
    file = open(file_path, 'a')
    file.write('第'+str(count)+'个\n')
    for i in list:
        file.write(i+'\n')
    file.write('\n\n')
    file.close()

path1 = 'http://bj.58.com/dog/1/?PGTID=0d3000fc-0000-13b1-ca3b-4d326020e12d&ClickID=1'
path0 = 'http://bj.58.com/dog/0/?PGTID=0d3000fc-0000-13b1-ca3b-4d326020e12d&ClickID=1'

urls0 = get_urls(path0,'0')
count = 0
for url0 in urls0:
    time.sleep(2)
    count = count + 1
    detail(url0,count,Hkind_judge = 0)

urls1 = get_urls(path1,'1')
count = 0
for url1 in urls1:
    time.sleep(2)
    count = count + 1
    detail(url1, count,Hkind_judge = 1)

其实个人和商家两个部分的网址也是可以用format来做到的,我这里一开始贪图简单没有用循环,但是后期发现很多地方要用到0、1两个变量(包括两份网址txt的命名和商品详情里出处的判断)

总结:

说一下上面难点的解决方案

  • 浏览人数:
    先打开检查,sources,刷个新,可以找到一个js文件,把这个地址复制下来,进行get请求就可以获得这个js浏览量的返回(即数据部分最后的total = xxxx)
    这个文件的Infoid一串数字和网址的id是一致的,因此把网址的id取出来,就可以用fomat批量获取js啦
    但是我还遇到一个问题,就是照做了之后还是爬不到,返回total=0,这个时候加个Header的Referer试试就可以


    打卡:大作业|爬取一页商品数据_第6张图片
    检查界面
def get_views(url):
    time.sleep(2)
    get_num1 = url.split('x.shtml')
    get_num2 = get_num1[0].split('dog/')[-1]
    api = 'http://jst1.58.com/counter?infoid={}'.format(get_num2)
    headers = {
        'Referer':'http://m.58.com/tj/dog/{}'.format(get_num2),
        'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1'
    }
    js = requests.get(api,headers = headers)
    views = js.text.split('total=')[-1]
    return views
  • 那么问题来了,要得到网址中的id,但是看我的结果txt,发现爬取的第一个网页是和下面不一样,没有一串数字的,这是因为这是跳转前的网址,我们无法判断得到的网址是否还有一次跳转,所以解决方法是取浏览量之前,取已打开详情页的网址进行分析(已跳转完成的)
respond = requests.get(path)
views = get_views(respond.url)
  • 不同网页完善的信息不同,所以一旦没有获取到需要的内容,程序会死掉哦~怎么解决这个问题呢,就因为这个调试到了今天(所以迟交了作业,抱歉)
    解决方法是要养成每个信息都要判断是否存在的习惯
    突然联系到直播的时候老师有个这样的习惯,在每个获取信息输出之前,会
    判断列表是否为空,像这样:if list else None
 list = [
        '网页分类:'+ url_kind[0].get_text() if url_kind else '网页分类:None',
        '网页标题:'+ title[0].get_text() if title else '网页标题:None',
        '发帖日期:'+ date[0].get_text() if date else '发帖日期:None',
        '店家类型:'+ host_kind,
        '浏览次数:'+ views,
        '宠物种类:'+ kind[0].get_text().replace('\t', '').replace('\n','').replace(' ','') if kind else '宠物种类:None',
        '主人:'+ host[0].get_text() if host else '主人:None',
        '电话:'+ phone[1].get_text() if len(phone) > 1  else '电话:None',
        '只数:'+ num[0].get_text() if num else '只数:None',
        '位置:'+ pos[0].get_text() if pos else '位置:None'
    ]

我用List存放是为了输出的txt格式好看一点,如果要输出其他的要用字典什么的啦
可以看到个人信息页面很多东西都是None因为他们没有写

  • 个人和商家版的详情页元素位置有轻微区别,这个导致我爬品种的时候,总是会一部份显示防疫的内容,一部分显示品种
    解决办法是判断当前是商家版还是个人版,改变位置,我这里用format(Hkind_judge是主人类型判断...名字奇怪了点哈)
kind = soup.select('body > div.content_quanbu > div.zhanshi_top.clearfix > div.zhanshi_top_l.fl.clearfix > div.col_sub.sumary.fl > ul > li:nth-of-type({}) > div.su_con > span:nth-of-type(1)'.format(3 if Hkind_judge == 0 else 4))
  • 爬取的文本有大量/t/r/n的信息,要怎么删除呢?(说的就是品种信息)
    删除的方法其实有很多种,我这里用了最粗暴的repla
'宠物种类:'+ kind[0].get_text().replace('\t', '').replace('\n','').replace(' ','') if kind else '宠物种类:None'
  • 挑战自己爬了下需要点击才能显示的电话号码
    然后我发现并不难,因为这个没有用js控制,直接就放在网页源码,所以只要判断好它的位置就可以,要小心的是所有select结果都是列表,没把握需要的信息在哪里的,就先print出来
    这里遇到的问题其实就是有的详情没有给电话,但是得到的列表依然是有一个值的,所以这里不是判断为空,而是判断元素个数是否大于1
'电话:'+ phone[1].get_text() if len(phone) > 1  else '电话:None'
  • 变量名比较混乱抱歉,因为会的单词实在不够多(捂脸),然后也忘了爬价格,不过我觉得价格应该也不难,所以就不管了
    这一次大作业确实发现了很多之前的网页不存在的问题,做起来比较辛苦,但是好有成就感~
    顺便为了保护,每个请求都加了time.sleep(2)保护,但是这个方法速度实在是慢了点
  • 嘛这周的作业也要加油了(づ ̄ 3 ̄)づ

你可能感兴趣的:(打卡:大作业|爬取一页商品数据)