美文网首页
来学爬虫吗?会坐牢的那种!

来学爬虫吗?会坐牢的那种!

作者: 小道萧兮 | 来源:发表于2019-12-31 22:13 被阅读0次

    〇、整个公司被抓

    2019 年的某一个工作日,公司员工像往常一样忙忙碌碌,某个程序员和产品经理正在为了一个需求争吵,小明带着耳机正坐在办公室敲代码。

    突然就来了一大群警察,要求所有人离开工位,双手离开电脑、手机等设备。整个公司的人都懵了,不知道发生了什么事情,但也都照办了。

    警察很快查封了公司的所有办公用品,问技术部相关人员要了服务器的信息,公司全体上下 200 多人无差别的全部送到看守所了解情况。

    在去看守所的路上,大家都还心里想这是不是搞错了,我们只是一个科技公司公司又没有骗人,怎么就集体被抓了。

    小明也一直认为自己没有犯罪,自己只是一名技术人员而已,所有的工作也都是按照领导要求来执行的,应该很快就会把我们释放了吧。

    随后,公司非核心人员都被释放了出来,主要集中在 HR、行政人员。最后确认公司 36 人被捕,其中大部分是程序员。

    被捕后小明委托的律师事务所,就是和我们交流的两位律师的事务所,据说小明入狱后就一直不认为自己有罪,也因一直拒绝认罪从而错过了取保候审的机会。

    目前小明还在等待最后的审判……

    好了,下面我们开始学 坐牢 爬虫。

    一、什么是爬虫

    其实你身边到处都是爬虫的产物,比如说搜索引擎 Google、百度,它们能为你提供这么多搜索结果,也都是因为它们或取了很多网页信息,然后展示给你。再来说一些商业爬虫,比如爬取淘宝的同类商品的价格信息,好为自己的商品挑选合适的价格。爬虫的用途很多很多,网上的信息成百上千,只要你懂爬虫,你都能轻松获取。

    二、爬取网页

    这里使用 Python3 来做爬虫,首先你得下载 Python3,如果你还没有安装可以看一看《Linux 安装 Python 3.x》

    然后还需要安装 Requests,这是一个 Python 的常用的外部模块,我们需要手动安装它。简单的方法,使用 pip 安装就好了。

    $ pip3 install requests
    

    现在我们已经可以开始爬取网页了。例如,我们爬取百度百科

    import requests
    
    URL = 'https://baike.baidu.com/item/%E7%BD%91%E7%BB%9C%E7%88%AC%E8%99%AB'
    
    # 使用 requests 发起 Get 请求获取网页数据
    request = requests.get(URL)
    # 使用 utf-8 编码,避免乱码
    request.encoding = 'utf-8'
    # 获取请求文本
    html = request.text
    print(html)
    

    经过这么几行代码,我们已经把百度百科的一个网页的信息全部爬取下来了。

    好,我们换一个 URL 爬取试试,比如说简书:https://www.jianshu.com/u/5fa5459c7b02。这时你会发现出问题了。

    403 Forbidden

    出现了 403 Forbidden,访问拒绝。这是因为简书发现你可能是通过爬虫来访问页面了,所以会出现拒绝。怎么办呢?

    我们可以使用伪装,就是伪装自己是浏览器,打开浏览器控制台,查看请求,可以在请求头中看到 user-agent 这一段:

    user-agent

    我们也仿照浏览器的请求,也就是在请求头中加入 user-agent,完整代码如下:

    import requests
    
    URL = 'https://www.jianshu.com/u/5fa5459c7b02'
    
    # 请求头
    headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.79 Safari/537.36'}
    # 使用 requests 发起 Get 请求获取网页数据
    request = requests.get(URL, headers=headers)
    # 使用 utf-8 编码,避免乱码
    request.encoding = 'utf-8'
    
    # 获取请求文本
    html = request.text
    print(html)
    

    现在可以成功爬取了。

    加入请求头只是一种简单的破解反爬技术,实际可能会遇到其他反爬取技术,这就需要具体问题具体分析了,比如请求带 cookie 等。当然加请求头的方法已经可以成功爬取大部分网页了。

    目前很多网站都增加了频率限制,一个 IP 在一定的时间内访问次数有限,最好不要去破解,一旦你的爬取频率过高,导致对方服务器瘫痪,这就是网络攻击了。

    三、获取需要的信息

    虽然把网页爬取下来了,但是有很多信息是我们不需要的。例如,我只想看看网页的标题,却把整个网页都爬取下来了。那怎么从整个网页中获取我们需要的内容呢?

    先安装 BeautifulSoup:

    $ pip3 install beautifulsoup4
    

    BeautifulSoup 是一个网页解析的工具,有了这个工具,就可以省去大量正则表达式,简化爬虫代码,快速获取我们需要的内容。还可以查看 Beautiful Soup 文档了解基本使用。

    接着再装一个 HTML 解析器就好了:

    $ pip3 install lxml
    

    准备完毕,可以开始了。例如我需要获取网页的标题,获取 <p> 标签,获取 <a> 标签的连接等。

    from bs4 import BeautifulSoup
    import requests
    
    URL = 'https://www.jianshu.com/u/5fa5459c7b02'
    headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.79 Safari/537.36'}
    
    # 使用 requests 发起 Get 请求获取网页数据
    request = requests.get(URL, headers=headers)
    request.encoding = 'utf-8'
    html = request.text
    
    # 使用 BeautifulSoup 解析网页
    soup = BeautifulSoup(html, 'lxml')
    
    # 获取网页 title
    title = soup.title.string
    
    # 获取 <p> 标签
    p = soup.find_all('p')
    
    # 获取 <a> 标签
    a = soup.find_all('a')
    
    # 获取 <a> 标签的链接
    # 因为 <a> 真正的 link 在 <a href="link"> 里面,href 也可以看做是 <a> 的一个属性,用字典的形式来读取
    a_href = [h['href'] for h in a]
    
    # 获取 class 为 'have-img' 的 <li> 标签
    img_li = soup.find_all('li', {'class': 'have-img'})
    # 获取 <li> 标签里面的 <img> 标签
    for li in img_li:
        imgs = li.find_all('img')
    

    到这里,已经小功告成,爬虫的基本使用已经介绍完毕,如果想爬取更符合自定义标准的信息,那么还得需要了解正则表达式才行。

    四、正则表达式

    正则表达式 (Regular Expression) 又称 RegEx,是用来匹配字符的一种工具。在一大串字符中寻找你需要的内容。它常被用在很多方面,比如网页爬虫,文稿整理,数据筛选等等。最简单的一个例子:

    <td>
        <img src="https://xxx/1.jpg">
        <img src="http://xxx/2.jpg">
        <img src="ftp://xxx/3.png">
    </td>
    

    我们只需要提取以 https 开头的 url,这该如何处理呢?这是我们就需要用到正则表达式了。

    正则表达式的大致匹配过程是:依次拿出表达式和文本中的字符比较,如果每一个字符都能匹配,则匹配成功;一旦有匹配不成功的字符则匹配失败。下面列出了正则表达式元字符和语法:

    • \d:任何数字,[0-9]
    • \D:不是数字,[^\d]
    • \s:任何空白字符, 如 [\t \n \r <空格>]
    • \S:不是空白字符
    • \w:任何大小写字母、数字,[a-zA-Z0-9]
    • \W:不是字母、数字,[^\w]
    • .:匹配任何字符 (除了 \n)
    • ^:匹配开头
    • $:匹配结尾
    • ?:匹配前一个字符0次或1次
    • *:匹配前一个字符0次或多次
    • +:匹配前一个字符1次或多次

    下面就是具体的举例说明:

    import re # 导入正则匹配模块
    
    # 匹配前一个字符1次或多次
    re.search(r"Mon(day)+", "Monday") # <re.Match object; span=(0, 6), match='Monday'>
    re.search(r"Mon(day)+", "Mon") # None
    re.search(r"Mon(day)*", "Mon") # <re.Match object; span=(0, 3), match='Mon'>
    
    # 匹配数字
    re.compile('\d').match('abc123') # <re.Match object; span=(3, 4), match='1'>
    re.compile('\d+').match('abc123') # <re.Match object; span=(3, 6), match='123'>
    
    # 匹配字母和数字
    re.compile('\w').match('abc123') # <re.Match object; span=(0, 1), match='a'>
    re.compile('\w+').match('abc123') # <re.Match object; span=(0, 6), match='abc123'>
    
    # 匹配开头
    re.compile('^abc').match('abc123') # <re.Match object; span=(0, 3), match='abc'>
    
    # 匹配结尾
    re.compile('123$').match('abc123') # <re.Match object; span=(3, 6), match='123'>
    

    通过这些简单的了解,我们就能知道以 https:// 开头的正则表达式应该怎么写了:

    re.compile('^(https://).+')
    

    其中 ^(https://) 表示以 https:// 开头,.匹配任意字符,+表示匹配任意次数,合并起来表示匹配以 https:// 开头,之后可以是任意字符出现任意次。完整代码如下:

    from bs4 import BeautifulSoup
    import requests
    import re # 导入正则匹配模块
    
    # 获取网页
    html = requests.get(URL).text
    
    # 使用 BeautifulSoup 解析网页
    soup = BeautifulSoup(html, 'lxml')
    
    # 获取 <img> 标签,并且 src 以 https:// 开头
    imgs = soup.find_all('img', {'src': re.compile('^(https://).*')})
    
    print(imge) # <img src="https://xxx/1.jpg">
    print(imge['src']) # https://xxx/1.jpg
    

    如果需要获取以 png 结尾的图片呢?只需要改一句话:

    img = soup.find_all('img', {'src': re.compile('.+(.png)$')})
    

    五、图片下载

    现在我们来做一个小练习,就是爬取 国家地理中文网 中一个页面上的所有图片,并且下载到本地。

    查看网页

    可以看到,图片是 <img> 标签,并且连接是以 http 开头,通过这样简单的分析,就可以开始下载图片了。

    由于网页可能会更新,代码并不是一成不变的,重点是了解下载方法,同时也可以根据自己的需求去下载。

    from bs4 import BeautifulSoup
    import requests
    import re
    import ssl
    import urllib
    
    # 解决访问 HTTPS 时不受信任 SSL 证书问题
    # ssl._create_default_https_context = ssl._create_unverified_context
    
    URL = "http://www.nationalgeographic.com.cn/animals"
    
    headers = {'User-Agent': 'Mozilla/5.0 3578.98 Safari/537.36'}
    
    request = requests.get(URL, headers=headers)
    request.encoding = 'utf-8'
    html = request.text
    soup = BeautifulSoup(html, 'lxml')
    
    # 获取 'http://' 或 'https://'  开头的 <img> 标签
    img_urls = soup.find_all('img', {'src': re.compile('^(http(s)?://).+')})
    for i in range(len(img_urls)):
        url = img_urls[i]['src']
        # 下载图片
        urllib.request.urlretrieve(url, '/Users/Desktop/Picture/' + str(i) + '.jpg')
    

    图片已经下载完成

    下载图片

    现在可以爬取一个网页上的图片了,如果我想把整个 国家地理中文网所有的图片都下载呢?

    import time
    from bs4 import BeautifulSoup
    import re
    import requests
    import urllib
    import ssl
    
    ssl._create_default_https_context = ssl._create_unverified_context
    
    base_url = 'http://www.ngchina.com.cn/animals/'
    headers = {'User-Agent': 'Mozilla/5.0 3578.98 Safari/537.36'}
    
    def crawl(url):
        '''
        爬取网页
        :param url: url
        :return: url and current web content
        '''
        request = requests.get(url, headers=headers)
        request.encoding = 'utf-8'
        html = request.text
        return url, html
    
    
    def parse(url, html):
        '''
        解析网页
        '''
        soup = BeautifulSoup(html, 'lxml')
        title = soup.title.string  # 获取网页标题
        # 获取当前页面上的链接,待下个循环爬取,只拿国家地理中文网的链接
        urls = soup.find_all('a', {"href": re.compile('^(http://www.ngchina).+')})
        page_urls = set([url['href'] for url in urls])  # 链接去重
        download_img(soup)  # 下载图片
        return title, url, page_urls
    
    
    def download_img(soup):
        """
        下载以 http 或 https 开头的图片
        """
        img_urls = soup.find_all('img', {'src': re.compile('^(http(s)?://).+')})
        for i in range(len(img_urls)):
            url = img_urls[i]['src']
            urllib.request.urlretrieve(url, '/Users/terry-jri/Desktop/Picture/' + str(time.time()) + '.jpg')
    
    
    # 待爬取网页url
    unseen = set([base_url, ])
    
    # 已爬取网页url
    seen = set()
    
    count, t1 = 1, time.time()
    
    while len(unseen) != 0:  # still get some url to visit
        if len(seen) > 100:  # max count
            break
    
        print('\nCrawling...')
        htmls = [crawl(url) for url in unseen]
    
        print('\nParsing...')
        results = [parse(url, html) for url, html in htmls]
    
        print('\nAnalysing...')
        seen.update(unseen)  # seen the crawled
        unseen.clear()  # nothing unseen
    
        for title, url, page_urls in results:
            print(count, title, url)
            count += 1
            unseen.update(page_urls - seen)  # get new url to crawl
    
    print('Total time: %.1f s' % (time.time() - t1,))
    
    

    相关文章

      网友评论

          本文标题:来学爬虫吗?会坐牢的那种!

          本文链接:https://www.haomeiwen.com/subject/nrnboctx.html