11

CPP数据组测试题分析——数据采集部分

 3 years ago
source link: https://zhuanlan.zhihu.com/p/33499442
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

CPP数据组测试题分析——数据采集部分

大家好,我是来自厦门大学16级经济学院的本科生,前段时间通过了数据组的测试,现在处于试用阶段。很高兴能够有机会和大家分享一下我自己关于CPP数据组测试题的一些心得。

下面我将重点和大家分享一下数据采集部分自己的一些心得体会,希望能够对后来者有一些帮助。当然作为一个新手,有很多不明白的地方,代码也并不好看,出现问题还请大家指正~

------------------------------------------正文开始--------------------------------------------

我是用的是urllib+正则表达式。推荐requests+beautifulsoup,据说很好用。

我们要爬取的部分是豆瓣读书,标签为经济学。链接如下:https://book.douban.com/tag/%E7%BB%8F%E6%B5%8E%E5%AD%A6

数据采集部分的要求:

需要进行数据爬取的地方有两处,第一是建议数据条目,第二是附加数据条目(额外加分项)。

建议数据条目:ID、书名、出版日期、评分(重要)、评价人数(重要)、纸质版价格(重要)、商品简介等。

附加数据条目:1.豆瓣网站列举的价格 2.各个网站价格

通过浏览参考文献可以发现,豆瓣为想要获得数据的人提供了接口,利用接口可以得到你想要的大部分数据而且不会被目标网站拒绝。接口文档链接:https://developers.douban.com/wiki/?title=api_v2

但是也有不能通过接口获取的部分,比如书籍简介,比如豆瓣网站上列举的其他网站价格。下面以简介为例来讨论。

不使用接口,也就是说我们不会光明正大的走正门了,那我们现在就是走后门的小偷了(滑稽)。豆瓣是有反爬措施的,所以爬虫要有一定的伪装,基(我)本(会)的方法有三个,即:

  • 设置headers
  • 设置时间间隔
  • 使用代理服务器

下面解释一下:

  • 首先是headers,即向目标服务器发送请求时,通过添加headers的信息来模拟浏览器的行为,浏览器的"request headers"中有很多信息。

headers如下:

headers = {
    "Host": "book.douban.com",
    "Connection": "keep-alive",
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36",
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
    "Accept-Language": "zh-CN,zh;q=0.8",
    "Referer": "https://book.douban.com/tag/%E7%BB%8F%E6%B5%8E%E5%AD%A6"
     }
headerall = []                                 #将headers转换成要求的格式,与urllib库有关
for (key, value) in headers.items():
     item = (key, value)
     headerall.append(item)
  • 设置时间间隔是为了让请求行为更像人在浏览网站,最好设置一个随机数。比如:
time.sleep(random.randint(3,7))    
  • 设置代理服务器是为了防止因用一个IP反复访问目标网站而使得自己的IP被目标网站屏蔽(然鹅有时还是会被屏蔽的,使用了代理自己的IP还是有可能被发现),可以用一个爬虫爬取一些代理备用,附上代码链接:https://github.com/shijuzheng/test-for-cpp/blob/master/proxy.py

除此之外还有其他的方法,CPP的专栏的一篇文章有讲,附上链接Python爬虫|深入请求(四)常见的反爬机制以及应对方法

可以开始写代码了

1.构造正则表达式

先打开几个网页观察一下,可以发现书籍简介部分在作者简介部分前面,下载网页源码观察一下,可以发现源码是这样的:

<div class="intro">
    <p>《经济学原理(第5版):微观经济学分册》......教辅资源。</p></div>

然后就可以构造正则表达式了,因为要匹配的内容在前面且只匹配一次,所以使用re.search():

pattern = '<div class="intro">(.+?)</p>'
intro = re.search(pattern, content, re.S).group(0)

2.构造url

通过观察目标网页的url可以发现他们都遵循相同的规律

"https://book.douban.com/subject/******/"

星号部分即为书的id,而在前面通过接口已经获得了所有并将信息保存为csv格式:

然后读取,将书的id保存到一个列表里:

import pandas as pd
IDlist=[]
op = pd.read_csv(filename)
opid = op['idlist']
idlist = list(opid)

之后便可以构造出url了:

baseurl="https://book.douban.com/subject/"
for id in idlist:
    url=baseurl+str(id)+"/"

3.设置cookie和代理,添加headers信息:

proxy = urllib.request.ProxyHandler({'http': proxy_addr})  #proxy_addr为具体的代理服务器的IP
cjar = http.cookiejar.CookieJar()
opener = urllib.request.build_opener(
    proxy,
    urllib.request.HTTPHandler(),
    urllib.request.HTTPCookieProcessor(cjar))
opener.addheaders = headerall
urllib.request.install_opener(opener)

遍历所有url抓取所需要的信息:

for i in range(len(idlist)):
    try:
        crawl(idlist[i], proxy_addr)
    except urllib.request.URLError as e:
        print(e)
        p = p + 1
        proxy_addr = proxylist[p]
    except BaseException:
        print(idlist[i])

except可以防止由于错误导致的程序中断并且打印错误信息。

每当出现错误时,就换一个代理ip继续爬取。

函数crawl(idlist[i], proxy_addr)代码如下:

def crawl(idnum, proxy_addr):
    url = baseurl + str(idlist[i]) + "/"
    cjar = http.cookiejar.CookieJar()
    proxy = urllib.request.ProxyHandler({'http': proxy_addr})
    opener = urllib.request.build_opener(
        proxy,
        urllib.request.HTTPHandler(),
        urllib.request.HTTPCookieProcessor(cjar))
    opener.addheaders = headerall
    urllib.request.install_opener(opener)
    content = urllib.request.urlopen(url).read().decode('utf-8')
    content = str(content)
    pattern = r'<div class="intro">(.+?)</p>'
    intro = re.search(pattern, content, re.S).group(0)
    intro=re.sub('<.+?>','',intro)
    with open('D:\\intro.txt', 'a') as fp:   #保存抓取的内容
        fp.writelines(intro)
        fp.writelines('\n')
    time.sleep(random.randint(3,7))  

结果如下:

附上所有代码网址链接:

https://github.com/shijuzheng/test-for-cpp/blob/master/intro.py

希望对想加入CPP的小伙伴有帮助。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK