<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 用Python 3开发网络爬虫
By Terrill Yang (Github
Step1:  可以发现，所有指向Wikipedia词条的链接都是/wiki/开头，所以我们可以用正则表达式来过滤出这些词条，就像这样
Step2:  上面的函数还不太能用于实际抓取，我们稍作改进，变成下面这个样子，就可以初步用于抓取页面的所有链接了。因为我们不能无限制地抓取下去，我便设置了10个链接的上限。
Step3:  为了避免一个页面被采集两次，链接去重是非常重要的，下面的代码用Python的set来保存已经采集的链接。下面这段代码将无限制地运行下去，除非set集为空，然而这几乎是不可能的。
Step4:  我们接下来可以写一个随机找外链的小程序
Step5:  如果我们的目标是获取页面上所有的外链，并记录之，我们可以用下面的函数来办
Step6:  使用Scrapy
写网络爬虫的挑战之一就是你要不断重复一些简单的任务：找出页面上所有的链接，区分外链和内链，跳转到新的页面。掌握这些基本模式非常有用，从零开始也是完全可行的，但是这里有一个工具可以帮助你自动处理这些细节。
https
<ASSISTANT_TASK:>
Python Code:
from urllib.request import urlopen
from bs4 import BeautifulSoup
html = urlopen("https://en.wikipedia.org/wiki/Python_(programming_language)")
bsObj = BeautifulSoup(html.read(), "html.parser")
for link in bsObj.findAll("a"):
    if 'href' in link.attrs:
        print(link.attrs['href'])
Explanation: 用Python 3开发网络爬虫
By Terrill Yang (Github: https://github.com/yttty)
用Python 3开发网络爬虫 - Chapter 06 开始采集
一个抓取Wikipedia的例子
我们从最简单的抓取开始，先写一段可以获取Wikipedia任何页面的代码：
End of explanation
from urllib.request import urlopen
from bs4 import BeautifulSoup
import re
html = urlopen("https://en.wikipedia.org/wiki/Python_(programming_language)")
bsObj = BeautifulSoup(html.read(), "html.parser")
for link in bsObj.find("div", {"id":"bodyContent"}).findAll("a", href=re.compile("^(/wiki/)((?!:).)*$")):
    if 'href' in link.attrs:
        print(link.attrs['href'])
Explanation: 可以发现，所有指向Wikipedia词条的链接都是/wiki/开头，所以我们可以用正则表达式来过滤出这些词条，就像这样
End of explanation
from urllib.request import urlopen
from bs4 import BeautifulSoup
import datetime
import random
import re
count = 0
random.seed(datetime.datetime.now())
def getLinks(articleUrl):
    html = urlopen("http://en.wikipedia.org"+articleUrl)
    bsObj = BeautifulSoup(html, "html.parser")
    return bsObj.find("div", {"id":"bodyContent"}).findAll("a", href=re.compile("^(/wiki/)((?!:).)*$"))
links = getLinks("/wiki/Python_(programming_language)")
while len(links) > 0 and count < 10:
    newArticle = links[random.randint(0, len(links)-1)].attrs["href"]
    print(newArticle)
    count = count + 1
    links = getLinks(newArticle)
Explanation: 上面的函数还不太能用于实际抓取，我们稍作改进，变成下面这个样子，就可以初步用于抓取页面的所有链接了。因为我们不能无限制地抓取下去，我便设置了10个链接的上限。
End of explanation
from urllib.request import urlopen
from bs4 import BeautifulSoup
import re
pages = set()
def getLinks(pageUrl):
    global pages
    html = urlopen("http://en.wikipedia.org"+pageUrl)
    bsObj = BeautifulSoup(html, "html.parser")
    try:
        print(bsObj.h1.get_text())
        print(bsObj.find(id ="mw-content-text").findAll("p")[0])
        print(bsObj.find(id="ca-edit").find("span").find("a").attrs['href'])
    except AttributeError:
        print("This page is missing something! No worries though!")
    
    for link in bsObj.findAll("a", href=re.compile("^(/wiki/)")):
        if 'href' in link.attrs:
            if link.attrs['href'] not in pages:
                #We have encountered a new page
                newPage = link.attrs['href']
                print("----------------\n"+newPage)
                pages.add(newPage)
                getLinks(newPage)
getLinks("") 
Explanation: 为了避免一个页面被采集两次，链接去重是非常重要的，下面的代码用Python的set来保存已经采集的链接。下面这段代码将无限制地运行下去，除非set集为空，然而这几乎是不可能的。
End of explanation
from urllib.request import urlopen
from bs4 import BeautifulSoup
import re
import datetime
import random
pages = set()
random.seed(datetime.datetime.now())
#Retrieves a list of all Internal links found on a page
def getInternalLinks(bsObj, includeUrl):
    internalLinks = []
    #Finds all links that begin with a "/"
    for link in bsObj.findAll("a", href=re.compile("^(/|.*"+includeUrl+")")):
        if link.attrs['href'] is not None:
            if link.attrs['href'] not in internalLinks:
                internalLinks.append(link.attrs['href'])
    return internalLinks
            
#Retrieves a list of all external links found on a page
def getExternalLinks(bsObj, excludeUrl):
    externalLinks = []
    #Finds all links that start with "http" or "www" that do
    #not contain the current URL
    for link in bsObj.findAll("a", href=re.compile("^(http|www)((?!"+excludeUrl+").)*$")):
        if link.attrs['href'] is not None:
            if link.attrs['href'] not in externalLinks:
                externalLinks.append(link.attrs['href'])
    return externalLinks
def splitAddress(address):
    addressParts = address.replace("http://", "").split("/")
    return addressParts
def getRandomExternalLink(startingPage):
    html = urlopen(startingPage)
    bsObj = BeautifulSoup(html, "html.parser")
    externalLinks = getExternalLinks(bsObj, splitAddress(startingPage)[0])
    if len(externalLinks) == 0:
        internalLinks = getInternalLinks(startingPage)
        return getNextExternalLink(internalLinks[random.randint(0, 
                                  len(internalLinks)-1)])
    else:
        return externalLinks[random.randint(0, len(externalLinks)-1)]
    
def followExternalOnly(startingSite):
    externalLink = getRandomExternalLink(startingSite)
    print("Random external link is: "+externalLink)
    #followExternalOnly(externalLink)
            
followExternalOnly("http://oreilly.com")
followExternalOnly("http://oreilly.com")
followExternalOnly("http://oreilly.com")
Explanation: 我们接下来可以写一个随机找外链的小程序
End of explanation
allExtLinks = set()
allIntLinks = set()
def getAllExternalLinks(siteUrl):
    html = urlopen(siteUrl)
    bsObj = BeautifulSoup(html.read(), "html.parser")
    internalLinks = getInternalLinks(bsObj, splitAddress(siteUrl)[0])
    externalLinks = getExternalLinks(bsObj, splitAddress(siteUrl)[0])
    for link in externalLinks:
        if link not in allExtLinks:
            allExtLinks.add(link)
            print(link)
    for link in internalLinks:
        if link not in allIntLinks:
            print(link)
            allIntLinks.add(link)
            getAllExternalLinks(link)
getAllExternalLinks("http://oreilly.com")
Explanation: 如果我们的目标是获取页面上所有的外链，并记录之，我们可以用下面的函数来办
End of explanation
import scrapy
class BlogSpider(scrapy.Spider):
    name = 'blogspider'
    start_urls = ['https://blog.scrapinghub.com']
    def parse(self, response):
        for title in response.css('h2.entry-title'):
            yield {'title': title.css('a ::text').extract_first()}
        next_page = response.css('div.prev-post > a ::attr(href)').extract_first()
        if next_page:
            yield scrapy.Request(response.urljoin(next_page), callback=self.parse)
Explanation: 使用Scrapy
写网络爬虫的挑战之一就是你要不断重复一些简单的任务：找出页面上所有的链接，区分外链和内链，跳转到新的页面。掌握这些基本模式非常有用，从零开始也是完全可行的，但是这里有一个工具可以帮助你自动处理这些细节。
https://scrapy.org
关于这个框架的使用，请参考其文档，这里不作更多叙述了。
如果以后有时间再继续补充。
End of explanation
<END_TASK>