新浪体育——篮球足球的直播和战报爬取

新浪体育——篮球足球的直播和战报爬取

用到的包的介绍

引用的包这里写图片描述

以上是基于python3.6.5的新浪体育直播间篮球足球的直播和战报爬取所引用到的全部内外部包,下面将会挑几个与爬虫息息相关的包进行介绍。

1.lxml

Python 标准库中自带了 xml 模块,但是性能不够好,而且缺乏一些人性化的 API,相比之下,第三方库 lxml 是用 Cython 实现的,而且增加了很多实用的功能,可谓爬虫处理网页数据的一件利器。lxml 大部分功能都存在 lxml.etree`中。

xml 是一个树形结构,lxml 使用etree._Elementetree._ElementTree来分别代表树中的节点和树。etree._Element 是一个设计很精妙的结构,可以把他当做一个对象访问当前节点自身的文本节点,可以把他当做一个数组,元素就是他的子节点,可以把它当做一个字典,从而遍历他的属性。

2.BeautifulSoup*

官方解释:

Beautiful Soup提供一些简单的、python式的函数用来处理导航、搜索、修改分析树等功能。它是一个工具箱,通过解析文档为用户提供需要抓取的数据,因为简单,所以不需要多少代码就可以写出一个完整的应用程序。

Beautiful Soup自动将输入文档转换为Unicode编码,输出文档转换为utf-8编码。你不需要考虑编码方式,除非文档没有指定一个编码方式,这时,Beautiful Soup就不能自动识别编码方式了。然后,你仅仅需要说明一下原始编码方式就可以了。

Beautiful Soup将复杂HTML文档转换成一个复杂的树形结构,每个节点都是Python对象,所有对象可以归纳为4种:

  • Tag
  • NavigableString
  • BeautifulSoup
  • Comment
Tag: HTML中的一个个标签
<title>The Dormouse's story</title>
<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>

HTML 标签加上里面包括的内容就是 Tag,它有两个重要的属性,是 name 和 attrs。

单独获取某个属性:

print (soup.p['class'])
#['title']
print (soup.p.get_text())
BeautifulSoup : 表示的是一个文档的全部内容

大部分时候,可以把它当作 Tag 对象,是一个特殊的 Tag,我们可以分别获取它的类型

Comment: 一个特殊类型的NavigableString对象,其实输出的内容仍然不包括注释符号

3.Selenium*

一种自动化测试工具。它支持各种浏览器,包括 Chrome,Safari,Firefox 等主流界面式浏览器,如果你在这些浏览器里面安装一个 Selenium 的插件,那么便可以方便地实现Web界面的测试。换句话说 Selenium 支持这些浏览器驱动。

声明浏览器对象:

chromedriver = 'C:\Program Files (x86)\Google\Chrome\Application\chromedriver.exe'
driver=webdriver.Chrome(chromedriver)

用selenium做自动化,有时候会遇到需要模拟鼠标操作才能进行的情况,比如单击、双击、点击鼠标右键、拖拽等等。而selenium给我们提供了一个类来处理这类事件——ActionChains

我在写代码的过程中参考了以下这篇博客:

https://blog.csdn.net/huilan_same/article/details/52305176

(selenium之 玩转鼠标键盘操作(ActionChains))

click(on_element=None) ——单击鼠标左键

click_and_hold(on_element=None) ——点击鼠标左键,不松开

context_click(on_element=None) ——点击鼠标右键

double_click(on_element=None) ——双击鼠标左键

drag_and_drop(source, target) ——拖拽到某个元素然后松开

drag_and_drop_by_offset(source, xoffset, yoffset) ——拖拽到某个坐标然后松开

key_down(value, element=None) ——按下某个键盘上的键

key_up(value, element=None) ——松开某个键

move_by_offset(xoffset, yoffset) ——鼠标从当前位置移动到某个坐标

move_to_element(to_element) ——鼠标移动到某个元素

move_to_element_with_offset(to_element, xoffset, yoffset) ——移动到距某个元素(左上角坐标)多少距离的位置

perform() ——执行链中的所有动作

release(on_element=None) ——在某个元素位置松开鼠标左键

send_keys(*keys_to_send) ——发送某个键到当前焦点的元素

send_keys_to_element(element, *keys_to_send) ——发送某个键到指定元素

Selenium还可以很方便地进行元素定位,可以参考以下博客:

https://blog.csdn.net/jojoy_tester/article/details/53453888

(selenium WebDriver定位元素学习总结

Selenium中元素定位共有八种

id

name

className

tagName

linkText

partialLinkText

xpath

cssSelector

详细可以参考:https://blog.csdn.net/kaka1121/article/details/51850881

4.其他

re——正则表达式工具
xlrd、xlwt——python中操作exel表格的工具
json——将 JavaScript 对象中表示的一组数据转换为字符串

新浪直播间网页结构介绍

首先进入新浪体育的官网首页,如下图所示,在最上面一排有一个直播,里面有我们需要爬取的内容:

新浪体育首页这里写图片描述

页面整洁,层次分明的新浪体育直播间:

新浪直播间这里写图片描述

打开这个界面我们直接跳到了当前的日期,同时我们可以看到在网页的右面有日期的选择器,最早可以选至2012年1月1日,可供数据的时间跨度可以说是非常大了,对比腾讯直播间的2015年,加上该网页构造较为简单,这是我们选择新浪体育直播间的主要原因。
建议使用Google的chrome浏览器打开该界面,因为chrome有非常丰富的开发者工具。现在让我们来看看当前网页的源码和构造。

TIM图片20180614104758这里写图片描述

网页中部的主要内容都在class为‘main clearfix’的一个大div标签内。其主要内容分为三个部分:
  • 默认选择全部直播的toptab板块
  • 罗列了当天所有比赛的topcont板块
  • 下方评论comment板块
    打开其中我们需要的第二个内容板块,再接着打开其中的比赛时间表的字段,依次打开main_data和cont_figure_list字段找到了我们需要的比赛的列表,其中次序为奇偶的比赛因为背景颜色不同class并不相同。

    TIM图片20180614110434这里写图片描述

TIM图片20180614110614这里写图片描述

每一场比赛在一个div结构下,于是接下来我们可以看一下每个比赛条目下具体的代码结构,并找出我们需要的部分和爬取方法了。

TIM图片20180614111025这里写图片描述

我们可以看到每个新闻目录下对应着在网页上横着罗列开的6个项目,这6个项目所代表的的部分我在图片中用红框标识了出来,开始的时候我决定用最左边图片的超链接的类来分别我所需要的篮球或足球体育类别,但是进一步点开观察每个比赛的直播和战报数据的时候发现cba的这两样数据很乱,于是选择了只用nba,而cba和nba最左边的标识是一样的,因此我选泽了直接判断第三个项目的文字是否是NBA来决定是否进入该比赛爬取数据。

TIM图片20180614112045这里写图片描述

于是就非常方便容易地判断出了该比赛的类别,但是我们注意到,电竞比赛前面也用到了足球图标,而且足球不只有世界杯欧洲杯等等而已,而是类别非常多,这就是为什么我们不能用图标类别,也不能直接判断类别标签是否等于某个值来确定这个比赛是否是足球。

TIM图片20180614112609这里写图片描述

我们可以发现网页中的图片,队名等几乎都是超链接,(可以说是非常用心非常便利了),其中我标出来的两部分,一个的超链接能够让我们进入这个比赛的细节,一个能够直接跳到战报界面,是我们需要用到的。
接下去是点击比分进入的比赛细节:TIM图片20180614124655这里写图片描述TIM图片20180614124655这里写图片描述
TIM图片20180614124655这里写图片描述
我们可以看到最上面是整场比赛的比分和概况,接下里默认是战报板块,基本上是一个标题一个图片配上一篇战报的格式。内部分为‘战报’,‘直播’,‘统计’,‘评论’四个板块。该板块由头部(
)中的四个超链接控制转换tab。 TIM图片20180614125415 这里写图片描述
直播界面对用户也十分友好,两边能够看到两队首发的全部数据情况,中间是按照时间顺序排列的场上情况的直播,除了中场休息等这样的信息以外大多是某队的一个球员跟上某个动作。这些列表式的直播数据使我们需要的,下方还有球队数据的对比等我们暂时不爬取。
1这里写图片描述
我们可以看到一大堆的直播数据每个由一个列表项表示,在标签属性里已经注明了,这条表述归属的队号和球员号,但是形式非常奇怪,有点像乱码,再打开看一下它们的内部结构:

2这里写图片描述

打开之后我们找到了我们可以爬取的队名和直播文字。而战报的文字当然直接在

标签内了。3这里写图片描述

同时我也发现,当我们改变直播大厅的日期的时候,网址只有scheduledate参数改变,如2018年6月12号的,网址为

http://match.sports.sina.com.cn/index.html#type=schedule&matchtype=all&filtertype=time&livetype=all&scheduledate=2018-06-12

然后我改变最后的日期参数就可以跳转到不同天的全部比赛,给我省了不少的时间。
以上就是新浪体育直播大厅结合源码有关页面的全部探索,于是就有了一个爬数据的基本思路。

代码与爬数据的步骤

1.准备

首先定义出一个webdriver,调用这个webdriver来打开模仿浏览器的行为访问数据。定义两个全局变量,page_list和cangoin,分别表示爬取数据的标号以及是否能够进入比赛细节页面爬取战报,我发现2014年7月以前的比赛只能进入单独的战报界面爬取战报,因为单场比赛界面里面的战报全部是空的,em。。可能是新浪后台的问题。选取2012年6月这一天的url方便进行测试。
chromedriver = 'C:\Program Files (x86)\Google\Chrome\Application\chromedriver.exe'
driver=webdriver.Chrome(chromedriver)
#driver=webdriver.Chrome()
global page_list
global cangoin
cangoin=0
page_return=1
driver.implicitly_wait(2)
url='http://match.sports.sina.com.cn/index.html#type=schedule&matchtype=all&filtertype=time&livetype=ed&scheduledate=2012-06-06'

2.挑出所有为NBA的比赛

首先用driver进入我们需要爬取数据的网页,定义出BeautifulSoup,然后按照刚才我找到的标识比赛类型的标签一层层向下找,用到了soup.find('标签名', 属性=值)soup.find_all('标签名', 属性=值) 的方法,其中第二个方法返回的是一个列表。为了做测试,我把所有不是NBA的比赛都输出在了控制台里,如果发现了对应的NBA比赛,就获取进入search方法进行战报和直播的爬取,如果是里面不能爬取战报的这类比赛,就要在该页面点击进入战报超链接的单独网页,我为了这种情况写了一个getzb的方法。所以isNBA的代码如下:
def isNBA(url):
    driver.get(url)
    #driver.get(url)
    soup = BeautifulSoup(driver.page_source, 'lxml')
    div_m = soup.find('div', class_='main_data')
    div_c = div_m.find('div', class_='cont_figure')
    div_l = div_c.find('div', class_='cont_figure_lis')
    div = div_l.find_all('div', recursive=False)
    for d in div:
        dd = d.find_all('div', recursive=False)[2]
        dp = dd.find('p')
        if dp.get_text()=='NBA':
            dzb = d.find_all('div', recursive=False)[3]
            dfl = dzb.find('div', class_='cont_figure_li03_m')
            span = dfl.find('span', class_='cRed')
            sa = span.find('a')
            print(sa['href'])
            url_into = sa['href']
            #print(21321)
            print(url_into)
            #print(213)
            search(url_into)
            print(cangoin)
            if not cangoin:
                p = dfl.find('p', recursive=False)
                a = p.find('a', text='战报')
                print (a['href'])
                url_zb = a['href']
                getzb(url_zb)
        else:
            print ('不在')
            print (dp.get_text())

3.进入单场比赛search战报直播

在进入单场比赛的界面后,默认就是战报板块,所以如果我设置的cangoin变量是1的话,就可以直接在当前界面爬取战报了,这里我设置了一个getnews的方法将爬数据和存储战报的这一块提出了search方法,接下来点击直播超链接,这里找到这个元素,因为没有id什么的,所以是直接复制那个元素的xpath找过去的,非常好用。点击后试图爬取直播却发现仅有
  1. 标签,但这个标签似乎是空的,里面所有的
  2. 标签都不见了,为此我debug了很久,具体见如下代码中好长一段的注释,最后发现直播里面这一大堆的数据是通过JavaScript动态加载过去的,于是我利用chrome开发者工具里的network找到了这个js文件,爬取后对它进行解码。并且发现每个js网址的网址中只有id部分不同,而这个id可以从当前网址中截取到,于是就非常容易获得这个js网站了,传给dejson()函数就好。

4这里写图片描述

5这里写图片描述

search的代码如下:
def search(url_into):
    print(1)
    print(url_into)
    driver.get(url_into)
    driver.get(url_into)
    global page_return
    global real_name
    global real_time
    soup = BeautifulSoup(driver.page_source, 'lxml')

    if cangoin:
        getnews(url_into)

    tab_zb = driver.find_element_by_xpath('/html/body/section[2]/div/div[1]/div[1]/a[4]').click()#'a[tab()="live"]'
    ActionChains(driver).click(tab_zb)

    #body = soup.find('body', ppccont='news')
    #print (body['class'])

    span = soup.find('span', class_='qq_spanoption')
    as_ = span.find('a', class_='qq_login_h')
    print (as_['href'])
    id = as_['href'][-10:]
    href='http://api.sports.sina.com.cn/pbp/?format=json&source=web&withhref=1&mid='+id+'&pid=&eid=0&dpc=1'
    de_json(href)
    #print (soup.prettify())
    a = soup.find('a', tab='live')
    print(a['class'])

    # div = soup.find('div', class_='ppc03_cast_cont', stype='auto')
    # print (div['scrolling'])
    # if(div!=None):
    #     #div = soup.find('div', class_='ppc03_cast_tabs clearfix')
    #     ol = div.find('ol', recursive=False)
    #     print (ol['class'])
    #     div_d = div.find('div', recursive=False)
    #     print (div_d['class'])
    #     guest = div_d.find('div', class_ ="ppc03_cast_select bselector01 fr")
    #     select = guest.find('select')
    #     option = select.find('option')
    #     print (select.name)
    #     #guest = div_d.find('a', tab = 'guest', recursive=True)
    #     print (guest.get_text())
    #     li = ol.find_all('li', recursive=False)
    #     li = ol.find_all(re.compile("^li"))
    #     divs = ol.find_all('div', class_ = 'ppc03_cast_score fr')
    #     #print (divs[0].get_text())
    #     #print (ol.descendants[0])
    #     for l in li:
    #         div1 = l.find('div', recursive=False)#, class_='ppc03_cast_time f1' c
    #         print ('哈哈哈哈哈')
    #         print (l['nid'],'hhhhhhhhhh')
    #         real_name.append(div1.get_text())
    #         print (div1)
    #     print('hehehe')
    #     print (real_name)
    # else:
    #     return
    # page_return=1

4.在单独的战报界面里爬取战报getzb

单独的战报界面只需要找到包含大段文字的部分就非常简单爬取文字了,因为战报的文字都非常规整地罗列在

标签内,然后定义一个文件夹路径,这里用到了page_list来作为战报的序号及文件名。用open()函数打开文件。

def getzb(url):
    global page_list
    driver.get(url)
    soup = BeautifulSoup(driver.page_source, 'lxml')
    db = soup.find('div', class_='blkContainerSblk')
    dbody = db.find('div', id='artibody')
    ps = dbody.find_all('p', recursive=False)
    page_list = page_list - 1
    write_path = 'D:\其他\战报\\' + str(page_list-1) + '.txt'
    fo = open(write_path, "w", encoding='utf-8')
    for p in ps:
        pt = p.get_text()
        print(pt)
        fo.write(pt.replace(' ', ''))
        fo.write('\n')
    fo.close()

5.解析json爬取直播

刚才我找到的json网站打开其实是这样的:

6这里写图片描述

在解码json的网站把这段json解码后看到了它真正的结构:

7这里写图片描述

json.load(json文件) 的方法可以解码json,让他变成右边的树状结构,然后我们可以用索引的方式找到需要的信息,这里我保存了五条可能用到的信息,分别是:队名,当前比赛时间,该条比赛描述,当前主队得分,客队得分。调用xlwt打开exel写入信息。
代码如下:
def de_json(url):
    driver.get(url)
    soup = BeautifulSoup(driver.page_source, 'lxml')
    print (soup.prettify())
    pre = soup.find('pre')
    json_t = pre.get_text()
    json_string = json.loads(json_t)
    #print (json_string)

    workbook = xlwt.Workbook()  # excle打开
    sheet1 = workbook.add_sheet('sheet1', cell_overwrite_ok=True)
    write_path = 'D:\其他\直播\\'+str(page_list-1)+'.xls'
    #page_list = page_list-1

    page_in_list = 0
    for i in json_string['result']['data']['pbp_msgs']:
        #ele = json_string[i]
        #print (i.key)
        print (i)
        print (json_string['result']['data']['pbp_msgs'][i]['team_name'])
        print (json_string['result']['data']['pbp_msgs'][i]['game_clock'])
        des = json_string['result']['data']['pbp_msgs'][i]['description']
        txt = re.sub(r'<.*?>','',des)
        #print(re.match(r'>[\u4e00-\u9fa5]*<', des))
        #if re.match(r'>[\u4e00-\u9fa5]*<', des):
            #txt = re.match(r'>[\u4e00-\u9fa5]*<', des)[1:-1] + re.match(r'a>[\u4e00-\u9fa5]*',des)[2:]
            #print('Yesyesyes')
        #else:
            #txt = des
        print (txt)
        print (json_string['result']['data']['pbp_msgs'][i]['home_score'])
        print (json_string['result']['data']['pbp_msgs'][i]['visitor_score'])
        #print (i['game_clock'])
        #print ('\n')

        sheet1.write(page_in_list, 0, json_string['result']['data']['pbp_msgs'][i]['team_name'])
        sheet1.write(page_in_list, 1, json_string['result']['data']['pbp_msgs'][i]['game_clock'])
        sheet1.write(page_in_list, 2, txt)
        sheet1.write(page_in_list, 3, json_string['result']['data']['pbp_msgs'][i]['home_score'])
        sheet1.write(page_in_list, 4, json_string['result']['data']['pbp_msgs'][i]['visitor_score'])
        page_in_list = page_in_list + 1
    workbook.save(write_path)
    page_in_list = page_in_list + 1

    #json=soup.prettify()
    #json_string = json.load(json)
    #for i in [0:565]

6.在单场比赛界面爬战报getnews()

与getzb()类似,代码如下:
def getnews(url):
    driver.get(url)
    soup = BeautifulSoup(driver.page_source, 'lxml')
    divc = soup.find('div', class_='barticle_content')
    ps = divc.find_all('p', recursive=False)
    write_path = 'D:\其他\战报\\'+str(page_list)+'.txt'
    fo = open(write_path, "w", encoding='utf-8')
    for p in ps:
        pt = p.get_text()
        print (pt)
        fo.write(pt.replace(' ', ''))
        fo.write('\n')
    fo.close()

7.调整时间

表示出所有的日期放入网址爬取不同天的数据。
for i in years[0:]:
    if (i == '2012' ):
        for m in mouth[5:]:  # 每次出问题记得更改
            if (m in ['01','03','05','07','08','10','12']):
                for n in days2[:]:
                     print(i + '-' + m + '-' + n)
                     print(
                        'http://match.sports.sina.com.cn/index.html#type=schedule&matchtype=all&filtertype=time&livetype=ed&scheduledate=' + i + '-' + m + '-' + n)
                     isNBA('http://match.sports.sina.com.cn/index.html#type=schedule&matchtype=all&filtertype=time&livetype=ed&scheduledate=' + i + '-' + m + '-' + n)

            elif (m in ['02']):
                for n in days4:
                    print(i + '-' + m + '-' + n)
                    isNBA(
                        'http://match.sports.sina.com.cn/index.html#type=schedule&matchtype=all&filtertype=time&livetype=ed&scheduledate=' + i + '-' + m + '-' + n)
            else:
                for n in days1:
                    if (m=='06' and n =='02'):
                        continue
                    print(i + '-' + m + '-' + n)
                    print(
                        'http://match.sports.sina.com.cn/index.html#type=schedule&matchtype=all&filtertype=time&livetype=ed&scheduledate=' + i + '-' + m + '-' + n)
                    isNBA(
                        'http://match.sports.sina.com.cn/index.html#type=schedule&matchtype=all&filtertype=time&livetype=ed&scheduledate=' + i + '-' + m + '-' + n)
    elif (i=='2016'):
        for m in mouth[5:]:  # 每次出问题记得更改
            if (m in ['01','03','05','07','08','10','12']):
                for n in days2[:]:
                     print(i + '-' + m + '-' + n)
                     print(
                        'http://match.sports.sina.com.cn/index.html#type=schedule&matchtype=all&filtertype=time&livetype=ed&scheduledate=' + i + '-' + m + 
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值