知识点一:urllib的详解及基本使用方法

  一、基本介绍

      urllib是python的一个获取url(Uniform Resource Locators,统一资源定址器)了,我们可以利用它来抓取远程的数据进行保存。

  二、什么是Urllib

      Python内置的Http请求库,包含四个模块:

        urllib.request 请求模块    (模拟浏览器,可以用来发送request和获取request的结果)

        urllib.error 异常处理模块  (包含了urllib.request产生的异常)

        urllib.parse url解析模块    工具模块(用来解析和处理UR),如:拆分、合并

        urllib.robotparser robots.txt    解析模块(用来解析页面的robots.txt文件) 

  三、urllib模块的基本使用

    1.urlopen方法

      urllib.request 模块提供了最基本的构造 HTTP 请求的方法,利用它可以模拟浏览器的一个请求发起过程,同时它还带有处理 authenticaton (授权验证), redirections (重定向), cookies (浏览器Cookies)以及其它内容。

基本方法:
  urllib.request.urlopen(url, data=None, [timeout, ]*, cafile=None, capath=None, cadefault=False, context=None) - url: 需要打开的网址
- data:Post提交的数据 - timeout:设置网站的访问超时时间   直接用urllib.request模块的urlopen()获取页面,page的数据格式为bytes类型,需要decode()解码,转换成str类型。

    urlopen返回对象提供方法:

    -         read() , readline() ,readlines() , fileno() , close() :对HTTPResponse类型数据进行操作

    -         info():返回HTTPMessage对象,表示远程服务器返回的头信息

    -         getcode():返回Http状态码。如果是http请求,200请求成功完成;404网址未找到

    -         geturl():返回请求的url

import urllib.request

request_url = 'http://www.baidu.com'           # 需要请求的URL地址
response = urllib.request.urlopen(request_url) # 发起请求
print(response.read().decode('utf-8')) # 打印响应的文本,并进行UTF-8解码

    参数管理

      data参数

      data 参数是可选的,如果要添加 data ,它要是字节流编码格式的内容,即 bytes 类型,通过 bytes() 函数可以进行转化,另外如果你传递了这个 data 参数,它的请求方式就不再是 GET 方式请求,而是 POST 。

import urllib.parse
import urllib.request
data = bytes(urllib.parse.urlencode({'word': 'hello'}), encoding= 'utf8')
response = urllib.request.urlopen('http://httpbin.org/post', data=data)
print(response.read())

      在这里我们传递了一个参数 word ,值是 hello 。它需要被转码成 bytes (字节流)类型。其中转字节流采用了 bytes() 方法,第一个参数需要是 str (字符串)类型,

        需要用 urllib.parse.urlencode() 方法来将参数字典转化为字符串。第二个参数指定编码格式,在这里指定为 utf8 。

      提交的网址是 httpbin.org ,它可以提供 HTTP 请求测试。 http://httpbin.org/post 这个地址可以用来测试 POST 请求,它可以输出请求和响应信息,其中就包含我们传递的 data 参数。

 b'{
"args":{},
"data":"",
"files":{},
"form":{"word":"hello"},
"headers":{"Accept-Encoding":"identity",
"Connection":"close",
"Content-Length":"",
"Content-Type":"application/x-www-form-urlencoded",
"Host":"httpbin.org",
"User-Agent":"Python-urllib/3.4"
},
"json":null,
"origin":"132.196.111.200",
"url":"http://httpbin.org/post"}\n'

运行结果

     我们传递的参数出现在了 form 中,这表明是模拟了表单提交的方式,以 POST 方式传输数据。

      timeout参数

        timeout 参数可以设置超时时间,单位为秒,意思就是如果请求超出了设置的这个时间还没有得到响应,就会抛出异常,如果不指定,就会使用全局默认时间。它支持 HTTP 、 HTTPS 、 FTP 请求。

import urllib.request  
response = urllib.request.urlopen("http://httpbin.org/get",timeout=1)
print(response.read())
 During handling of the above exception, another exception occurr
ed:
Traceback (most recent call last): File "/var/py/python/urllibte
st.py", line 4, in <module> response = urllib.request.urlopen('h
ttp://httpbin.org/get', timeout=1)
...
urllib.error.URLError: <urlopen error timed out>

运行结果

      在这里我们设置了超时时间是1秒,程序1秒过后服务器依然没有响应,于是抛出了 urllib.error.URLError 异常,错误原因是 timed out 。因此我们可以通过设置这个超时时间来控制

        一个网页如果长时间未响应就跳过它的抓取,利用 try,except 语句就可以实现这样的操作。

import urllib.request
import socket
import urllib.error
try:
response = urllib.request.urlopen('http://httpbin.org/get',timeout=0.1)
except urllib.error.URLError as e:
if isinstance(e.reason, socket.timeout):
print("Time out!")

      在这里我们请求了 http://httpbin.org/get 这个测试链接,设置了超时时间是0.1秒,然后捕获了 urllib.error.URLError 这个异常,然后判断异常原因是超时异常,就得出它确实是因为超时而报错,

        打印输出了 TIME OUT ,当然你也可以在这里做其他的处理。

      常理来说,0.1秒内基本不可能得到服务器响应,因此输出了 TIME OUT 的提示。这样,我们可以通过设置 timeout 这个参数来实现超时处理,有时还是很有用的。

    2.request方法的使用

      由上我们知道利用 urlopen() 方法可以实现最基本的请求发起,但这几个简单的参数并不足以构建一个完整的请求,如果请求中需要加入 headers 等信息,我们就可以利用更强大的 Request 类来构建一个请求。

      首先我们用一个实例来感受一下 Request 的用法:

import urllib.request

request =urllib.request.Request("https://www.baidu.com")
response = urllib.request.urlopen(request)
print(response.read().decode("utf-8"))

      可以发现,我们依然是用 urlopen() 方法来发送这个请求,只不过这次 urlopen() 方法的参数不再是一个URL,而是一个 Request ,通过构造这个这个数据结构,一方面我们可以将请求独立成一个对象,

        另一方面可配置参数更加丰富和灵活。
      下面我们看一下 Request 都可以通过怎样的参数来构造,它的构造方法如下。

class urllib.request.Request(url, data=None, headers={}, origin_req_host=None, unverifiable=False, method=None)
     ---使用request()来包装请求,再通过urlopen()获取页面。

      第一个参数是请求链接,这个是必传参数,其他的都是可选参数。

      data :参数如果要传必须传 bytes (字节流)类型的,如果是一个字典,可以先用 urllib.parse.urlencode() 编码。

      headers: 参数是一个字典,你可以在构造 Request 时通过 headers 参数传递,也可以通过调用 Request 对象的 add_header() 方法来添加请求头。请求头最常用的用法就是通过修改 User-Agent 来伪装浏览器,默认的 User-
      Agent: 是 Python-urllib ,你可以通过修改它来伪装浏览器,比如要伪装火狐浏览器,你可以把它设置为 Mozilla/5.0 (X11; U; Linux i686)Gecko/20071127 Firefox/2.0.0.11

      origin_req_host: 指的是请求方的 host 名称或者 IP 地址。

      unverifiable: 指的是这个请求是否是无法验证的,默认是 False 。意思就是说用户没有足够权限来选择接收这个请求的结果。例如我们请求一个HTML文档中的图片,但是我们没有自动抓取图像的权限,这时 unverifiable 的值就是 True 。

      method: 是一个字符串,它用来指示请求使用的方法,比如 GET , POST , PUT 等等。
    下面我们传入多个参数构建一个 Request 来感受一下:

from urllib import request,parse
url = "http://httpbin.org/post"
headers = {
#伪装一个火狐浏览器
"User-Agent":'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)',
"host":'httpbin.org'
}
dict = {
"name":"Germey"
}
data = bytes(parse.urlencode(dict),encoding="utf8")
req = request.Request(url=url,data=data,headers=headers,method="POST")
response = request.urlopen(req)
print(response.read().decode("utf-8"))

    另一种方式:

from urllib import request,parse

url = "http://httpbin.org/post"
dict = {
"name":"Germey"
} data = bytes(parse.urlencode(dict),encoding="utf8")
req = request.Request(url=url,data=data,method="POST")
req.add_header('User-Agent','Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)')
response = request.urlopen(req)
print(response.read().decode("utf-8"))

    在这里我们通过四个参数构造了一个 Request , url 即请求链接,在 headers 中指定了 User-Agent 和 Host ,传递的参数 data 用了 urlencode() 和 bytes() 方法来转成字节流,另外指定了请求方式为 POST 。

  运行结果:

 {
"args":{},
"data":"",
"files":{},
"form":{"name":"Germey"},
"headers":{"Accept-Encoding":"identity",
"Connection":"close",
"Content-Length":"",
"Content-Type":"application/x-www-form-urlencoded",
"Host":"httpbin.org",
"User-Agent":"Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)"},
"json":null,
"origin":"139.196.111.200"
,"url":"http://httpbin.org/post"
}

运行结果测试

    另外 headers 也可以用 add_header() 方法来添加。

req = request.Request(url=url, data=data, method='POST')

req.add_header('User-Agent', 'Mozilla/4.0 (compatible; MSIE 5.5;Windows NT)')

    如此一来,我们就可以更加方便地构造一个 Request ,实现请求的发送。

    另一实例:

import urllib.request

url ='http://www.lagou.com/zhaopin/Python/?labelWords=label'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) '
'Chrome/45.0.2454.85 Safari/537.36 115Browser/6.0.3',
'Referer': 'http://www.lagou.com/zhaopin/Python/?labelWords=label',
'Connection': 'keep-alive',
"host":'www.lagou.com'
}
req = urllib.request.Request(url, headers=headers)
page = urllib.request.urlopen(req).read()
print(page.decode())

    用来包装头部的数据:

    -         User-Agent :这个头部可以携带如下几条信息:浏览器名和版本号、操作系统名和版本号、默认语言

    -         Referer:可以用来防止盗链,有一些网站图片显示来源http://***.com,就是检查Referer来鉴定的

    -         Connection:表示连接状态,记录Session的状态。

    3.响应

      a.响应类型实例:

import urllib.request

response=urllib.request.urlopen('https://www.python.org')
print(type(response))

        输出结果为:

<class 'http.client.HTTPResponse'>

      b.状态码,响应头实例:

import urllib.request

response = urllib.request.urlopen('http://www.python.org')
print(response.status) #正确返回200
print(response.getheaders()) #返回请求头
print((response.getheader('Servers'))

        输出结果:

200
[('Server', 'nginx'), ('Content-Type', 'text/html; charset=utf-8'), ('X-Frame-Options', 'SAMEORIGIN'), ('x-xss-protection', '1; mode=block'), ('X-Clacks-Overhead', 'GNU Terry Pratchett'),
('Via', '1.1 varnish'), ('Content-Length', ''), ('Accept-Ranges', 'bytes'), ('Date', 'Thu, 31 May 2018 08:49:02 GMT'), ('Via', '1.1 varnish'), ('Age', ''), ('Connection', 'close'),
('X-Served-By', 'cache-iad2127-IAD, cache-nrt6138-NRT'), ('X-Cache', 'HIT, HIT'), ('X-Cache-Hits', '1, 40'), ('X-Timer', 'S1527756542.308043,VS0,VE0'), ('Vary', 'Cookie'), ('Strict-Transport-Security', 'max-age=63072000; includeSubDomains')]

    3.handler(urllib.request高级特性)

     大家有没有发现,在上面的过程中,我们虽然可以构造 Request ,但是一些更高级的操作,比如 Cookies 处理,代理该怎样来设置?
     接下来就需要更强大的工具 Handler 登场了。

      简而言之你可以把它理解为各种处理器,有专门处理登录验证的,有处理 Cookies 的,有处理代理设置的,利用它们我们几乎可以做到任何 HTTP 请求中所有的事情。

    首先介绍下 urllib.request.BaseHandler ,它是所有其他 Handler 的父类,它提供了最基本的 Handler 的方法,例
      如 default_open() 、 protocol_request() 等。
    接下来就有各种 Handler 类继承这个 BaseHandler ,列举如下:

  • HTTPDefaultErrorHandler 用于处理HTTP响应错误,错误都会抛出 HTTPError 类型的异常。
  • HTTPRedirectHandler 用于处理重定向。
  • HTTPCookieProcessor 用于处理 Cookie 。
  • ProxyHandler 用于设置代理,默认代理为空。
  • HTTPPasswordMgr 用于管理密码,它维护了用户名密码的表。
  • HTTPBasicAuthHandler 用于管理认证,如果一个链接打开时需要认证,那么可以用它来解决认证问题。 另外还有其他的 Handler ,可以参考官方文档

  它们怎么来使用,不用着急,下面会有实例为你演示。
  另外一个比较重要的就是 OpenerDirector ,我们可以称之为 Opener ,我们之前用过 urllib.request.urlopen() 这个方法,实际上它就是一个 Opener 。
  那么为什么要引入 Opener 呢?因为我们需要实现更高级的功能,之前我们使用的 Request 、 urlopen() 相当于类库为你封装好了极其常用的请求方法,利用它们两个我们就可以完成基本的请求,但是现在不一样了,

    我们需要实现更高级的功能,所以我们需要深入一层,使用更上层的实例来完成我们的操作。所以,在这里我们就用到了比调用 urlopen() 的对象的更普遍的对象,也就是 Opener 。

  Opener 可以使用 open() 方法,返回的类型和 urlopen() 如出一辙。那么它和 Handler 有什么关系?简而言之,就是利用 Handler 来构建 Opener 。

   实例认证:

import urllib.request
auth_handler = urllib.request.HTTPBasicAuthHandler()
auth_handler.add_password(realm='PDQ Application',
uri='https://mahler:8092/site-updates.py',
user='klem',
passwd='kadidd!ehopper')
opener = urllib.request.build_opener(auth_handler)
urllib.request.install_opener(opener)
urllib.request.urlopen('http://www.example.com/login.html')

    此处代码为实例代码,用于说明 Handler 和 Opener 的使用方法。在这里,首先实例化了一个 HTTPBasicAuthHandler 对象,然后利用 add_password() 添加进去用户名和密码,相当于建立了一个处理认证的处理器。
    接下来利用 urllib.request.build_opener() 方法来利用这个处理器构建一个 Opener ,那么这个 Opener 在发送请求的时候就具备了认证功能了。接下来利用 Opener 的 open() 方法打开链接,就可以完成认证了。

      a.代理

import urllib.request

proxy_handler =urllib.request.ProxyHandler({

    'http':'http://127.0.0.1:4623',
'https':'https://127.0.0.1:4623' }) opener =urllib.request.build_opener(proxy_handler)
response= opener.open('http://www.baidu.com') print(response.read())

      在这里使用了 ProxyHandler , ProxyHandler 的参数是一个字典,key是协议类型,比如 http 还是 https 等,value是代理链接,可以添加多个代理。
      然后利用 build_opener() 方法利用这个 Handler 构造一个 Opener ,然后发送请求即可。

      b.cooike设置(用来维持登陆状态)

import http.cookiejar,urllib.request

cookie = http.cookiejar.CookieJar()
handler=urllib.request.HTTPCookieProcessor(cookie)
opener = urllib.request.build_opener(handler)
response = opener.open('http://www.baidu.com') for item in cookie: print(item.name+"="+item.value)

        首先我们必须声明一个 CookieJar 对象,接下来我们就需要利用 HTTPCookieProcessor 来构建一个 handler ,最后利用 build_opener 方法构建出 opener ,执行 open() 即可。     

        运行结果:

BAIDUID=A79E42193AF3BFA2D5EC5E46EB94667E:FG=1
BIDUPSID=97EE42193AF3BFA2D5EC5E96EB54667E
H_PS_PSSID=1953_21099_26950_26431_20930
PSTM=1529797975
BDSVRTM=0
BD_HOME=0

      可以看出输出了每一条 Cookie 的名称还有值。
      不过既然能输出,那可不可以输出成文件格式呢?我们知道很多 Cookie 实际也是以文本形式保存的。
      我们用下面的实例来感受一下: 

import http.cookiejar,urllib.request
filename = 'cookie.txt'
cookie = http.cookiejar.MozillaCookieJar(filename)
handler = urllib.request.HTTPCookieProcessor(cookie)
opener = urllib.request.build_opener(handler)
response = opener.open('http://www.baidu.com')
cookie.save(ignore_discard=True, ignore_expires=True)

    这时的 CookieJar 就需要换成 MozillaCookieJar ,生成文件时需要用到它,它是 CookieJar 的子类,可以用来处理 Cookie 和文件相关的事件,读取和保存 Cookie ,它可以将 Cookie 保存成 Mozilla 型的格式。
    运行之后可以发现生成了一个 cookie.txt 文件。

    结果如下:

# Netscape HTTP Cookie File
# http://curl.haxx.se/rfc/cookie_spec.html
# This is a generated file! Do not edit.
.baidu.com TRUE / FALSE 3622386254 BAIDUID 05A
E39B5F56C1DEC474325CDA522D44F:FG=1
.baidu.com TRUE / FALSE 3622386254 BIDUPSID 05
AE39B5F56C1DEC474325CDA522D44F
.baidu.com TRUE / FALSE H_PS_PSSID 19638_1453
_17710_18240_21091_18560_17001_21191_21161
.baidu.com TRUE / FALSE 3622386254 PSTM 147490
2606
www.baidu.com FALSE / FALSE BDSVRTM 0
www.baidu.com FALSE / FALSE BD_HOME 0

    另外还有一个 LWPCookieJar ,同样可以读取和保存 Cookie ,但是保存的格式和 MozillaCookieJar 的不一样,它会保存成与libwww-perl的Set-Cookie3文件格式的 Cookie 。
    那么在声明时就改为

cookie = http.cookiejar.LWPCookieJar(filename)

    运行结果:

#LWP-Cookies-2.0
Set-Cookie3: BAIDUID="0CE9C56F598E69DB375B7C294AE5C591:FG=1"; path="/"; domain=".baidu.com"; path_spec; domain_dot; expires="
4-10-14 18:25:19Z"; version=0
Set-Cookie3: BIDUPSID=0CE9C56F598E69DB375B7C294AE5C591; path="/"; domain=".baidu.com"; path_spec; domain_dot; expires="2084-10-1
4 18:25:19Z"; version=0
Set-Cookie3: H_PS_PSSID=20048_1448_18240_17944_21089_21192_21161_20929; path="/"; domain=".baidu.com"; path_spec; domain_dot; di
scard; version=0
Set-Cookie3: PSTM=1474902671; path="/"; domain=".baidu.com"; path_spec; domain_dot; expires="2084-10-14 18:25:19Z"; version=0
Set-Cookie3: BDSVRTM=0; path="/"; domain="www.baidu.com"; path_spec; discard; version=0
Set-Cookie3: BD_HOME=0; path="/"; domain="www.baidu.com"; path_spec; discard; version=0

    那么生成了 Cookie 文件,怎样从文件读取并利用呢?
    下面我们以 LWPCookieJar 格式为例来感受一下:

import http.cookiejar,urllib.request
cookie = http.cookiejar.LWPCookieJar()
cookie.load('cookie.txt', ignore_discard=True, ignore_expires=True)
handler = urllib.request.HTTPCookieProcessor(cookie)
opener = urllib.request.build_opener(handler)
response = opener.open('http://www.baidu.com')
print(response.read().decode('utf-8'))

     前提是我们首先利用上面的方式生成了 LWPCookieJar 格式的 Cookie ,然后利用 load() 方法,传入文件名称,后面同样的方法构建 handler 和 opener 即可。
    运行结果正常输出百度网页的源代码。

    4.异常处理

      a.  例一

from urllib import reqeust,error

 try:
response =request.urlopen('http://cuiqingcai.com/index.htm') 
except error.URLError as e:
print(e.reason)

      b.例二

from urllib import reqeust,error

try:
response =request.urlopen('http://cuiqingcai.com/index.htm') 
except error.HTTPError as e:
print(e.reason,e.code,e.headers,sep='\n')  
except error.URLError as e:
print(e.reason)  
else:
print('Request Successfully')

      c.例三

PYTHON 爬虫笔记二:Urllib库基本使用的更多相关文章

  1. Python爬虫入门之Urllib库的高级用法

    1.设置Headers 有些网站不会同意程序直接用上面的方式进行访问,如果识别有问题,那么站点根本不会响应,所以为了完全模拟浏览器的工作,我们需要设置一些Headers 的属性. 首先,打开我们的浏览 ...

  2. Python爬虫入门之Urllib库的基本使用

    那么接下来,小伙伴们就一起和我真正迈向我们的爬虫之路吧. 1.分分钟扒一个网页下来 怎样扒网页呢?其实就是根据URL来获取它的网页信息,虽然我们在浏览器中看到的是一幅幅优美的画面,但是其实是由浏览器解 ...

  3. PYTHON 爬虫笔记三:Requests库的基本使用

    知识点一:Requests的详解及其基本使用方法 什么是requests库 Requests库是用Python编写的,基于urllib,采用Apache2 Licensed开源协议的HTTP库,相比u ...

  4. Python爬虫入门:Urllib库的基本使用

    1.分分钟扒一个网页下来 怎样扒网页呢?其实就是根据URL来获取它的网页信息,虽然我们在浏览器中看到的是一幅幅优美的画面,但是其实是由浏览器解释才呈现出来的,实质它 是一段HTML代码,加 JS.CS ...

  5. 芝麻HTTP:Python爬虫入门之Urllib库的基本使用

    1.分分钟扒一个网页下来 怎样扒网页呢?其实就是根据URL来获取它的网页信息,虽然我们在浏览器中看到的是一幅幅优美的画面,但是其实是由浏览器解释才呈现出来的,实质它是一段HTML代码,加 JS.CSS ...

  6. Python爬虫入门:Urllib库的高级使用

    1.设置Headers 有些网站不会同意程序直接用上面的方式进行访问,如果识别有问题,那么站点根本不会响应,所以为了完全模拟浏览器的工作,我们需要设置一些Headers 的属性. 首先,打开我们的浏览 ...

  7. python爬虫笔记----4.Selenium库(自动化库)

    4.Selenium库 (自动化测试工具,支持多种浏览器,爬虫主要解决js渲染的问题) pip install selenium 基本使用 from selenium import webdriver ...

  8. PYTHON 爬虫笔记七:Selenium库基础用法

    知识点一:Selenium库详解及其基本使用 什么是Selenium selenium 是一套完整的web应用程序测试系统,包含了测试的录制(selenium IDE),编写及运行(Selenium ...

  9. 芝麻HTTP: Python爬虫入门之Urllib库的高级用法

    1.设置Headers 有些网站不会同意程序直接用上面的方式进行访问,如果识别有问题,那么站点根本不会响应,所以为了完全模拟浏览器的工作,我们需要设置一些Headers 的属性. 首先,打开我们的浏览 ...

随机推荐

  1. 笔记-迎难而上之Java基础进阶5

    Lambda表达式无参数无返回值的练习 //定义一个接口 public interface Cook{ public abstract void makeFood(); } public class ...

  2. android studio C/C++ jni 编写以及调试方法

    原文路径: http://blog.sina.com.cn/s/blog_ad64b8200102vnxl.html 目录 开发环境 2 编写hello_jni程序 2 运行结果 10 调试程序 10 ...

  3. 【jQuery】方法和选择器的双重使用详解

    1.jQuery选择直接子节点+除了某个元素 1>方法 $(".begon").children(".row:not(.moreDetail)") 2&g ...

  4. String空格删除和java删除字符串最后一个字符的几种方法

    1. String.trim()trim()是去掉首尾空格2.str.replace(" ", ""); 去掉所有空格,包括首尾.中间复制代码 代码如下:Str ...

  5. JSP自己定义标签继承哪个类

    JSP自己定义标签继承哪个类 解:JSP自己定义标签继承TagSupport

  6. C++11 并发指南二(std::thread 详解)(转)

    上一篇博客<C++11 并发指南一(C++11 多线程初探)>中只是提到了 std::thread 的基本用法,并给出了一个最简单的例子,本文将稍微详细地介绍 std::thread 的用 ...

  7. 【java读书笔记】——Collection集合之六大接口(Collection、Set、List、Map、Iterator和Comparable)

    两个月之前准备软考时,简单的从理论上总结了最经常使用的数据结构和算法,比方:线性表,链表,图.在进行java开发时,jdk为我们提供了一系列对应的类来实现主要的数据结构.jdk所提供的容器API位于j ...

  8. Oracle pipe

    初次接触到Report ,看到了它背后复杂的SQL操作, 首先看到了一个Pipe,先了解下PIPE的用法: 关键字PIPELINED表明这是一个oracle管道函数,oracle管道函数的返回值类型必 ...

  9. Codeforces Round #267 (Div. 2) B. Fedor and New Game

    After you had helped George and Alex to move in the dorm, they went to help their friend Fedor play ...

  10. Coding/Github/Bitbucket 地址

    Coding:https://coding.net/u/OberonTony Github:https://github.com/Oberon-Tonya Bitbucket:https://bitb ...