mirror of
https://github.com/kennethreitz/dive-into-python3.git
synced 2026-06-05 15:00:18 +00:00
some progress on httplib2-beyond-get section
This commit is contained in:
+149
-49
@@ -636,61 +636,140 @@ user-agent: Python-httplib2/$Rev: 259 $'
|
||||
|
||||
<h2 id=beyond-get>Beyond HTTP GET</h2>
|
||||
|
||||
<p>FIXME
|
||||
<p><abbr>HTTP</abbr> web services are not limited to <code>GET</code> requests. What if you want to create something new? Whenever you post a comment on a discussion forum, update your weblog, publish your status on a microblogging service like <a href=http://twitter.com/>Twitter</a> or <a href=http://identi.ca/>Identi.ca</a>, you’re probably already using <abbr>HTTP</abbr> <code>POST</code>.
|
||||
|
||||
<pre>
|
||||
<samp class=p>>>> </samp><kbd class=pp>import httplib2</kbd>
|
||||
<p>Both Twitter and Identi.ca both offer a simple <abbr>HTTP</abbr>-based <abbr>API</abbr> for publishing and updating your status in 140 characters or less. Let’s look at <a href=http://laconi.ca/trac/wiki/TwitterCompatibleAPI>Identi.ca’s <abbr>API</abbr> documentation</a> for updating your status:
|
||||
|
||||
<blockquote class=pf>
|
||||
<p><b>Identi.ca <abbr>REST</abbr> <abbr>API</abbr> Method: statuses/update</b><br>
|
||||
Updates the authenticating user’s status. Requires the <code>status</code> parameter specified below. Request must be a <code>POST</code>.
|
||||
|
||||
<dl>
|
||||
<dt><abbr>URL</abbr>
|
||||
<dd><code>https://identi.ca/api/statuses/update.<i><var>format</var></i></code>
|
||||
<dt>Formats
|
||||
<dd><code>xml</code>, <code>json</code>, <code>rss</code>, <code>atom</code>
|
||||
<dt><abbr>HTTP</abbr> Method(s)
|
||||
<dd><code>POST</code>
|
||||
<dt>Requires Authentication
|
||||
<dd>true
|
||||
<dt>Parameters
|
||||
<dd><code>status</code>. Required. The text of your status update. <abbr>URL</abbr>-encode as necessary.
|
||||
</dl>
|
||||
</blockquote>
|
||||
|
||||
<p>How does this work? To publish a new message on Identi.ca, you need to issue an <abbr>HTTP</abbr> <code>POST</code> request to <code>http://identi.ca/api/statuses/update.<i>format</i></code>. (The <var>format</var> bit is not part of the <abbr>URL</abbr>; you replace it with the data format you want the server to return in response to your request. So if you want a response in <abbr>XML</abbr>, you would post the request to <code>https://identi.ca/api/statuses/update.xml</code>.) The request needs to include a parameter called <code>status</code>, which contains the text of your status update. And the request needs to be authenticated.
|
||||
|
||||
<p>Authenticated? Sure. To update your status on Identi.ca, you need to prove who you are. Identi.ca is not a wiki; only you can update your own status. Identi.ca uses <a href=http://en.wikipedia.org/wiki/Basic_access_authentication><abbr>HTTP</abbr> Basic Authentication</a> (<i>a.k.a.</i> <a href=http://www.ietf.org/rfc/rfc2617.txt>RFC 2617</a>) over <abbr>SSL</abbr> to provide secure but easy-to-use authentication. <code>httplib2</code> supports both <abbr>SSL</abbr> and <abbr>HTTP</abbr> Basic Authentication, so this part is easy.
|
||||
|
||||
<p>A <code>POST</code> request is different from a <code>GET</code> request, because it includes a <i>payload</i>. The payload is the data you want to send to the server. The once piece of data that this <abbr>API</abbr> method requires is <code>status</code>, and it should be <i><abbr>URL</abbr>-encoded</i>. This is a very simple serialization format that takes a set of key-value pairs (<i>i.e.</i> a <a href=native-datatypes.html#dictionaries>dictionary</a>) and transforms it into a string.
|
||||
|
||||
<pre class=screen>
|
||||
<a><samp class=p>>>> </samp><kbd class=pp>from urllib.parse import urlencode</kbd> <span class=u>①</span></a>
|
||||
<a><samp class=p>>>> </samp><kbd class=pp>data = {'status': 'Test update from Python 3'}</kbd> <span class=u>②</span></a>
|
||||
<a><samp class=p>>>> </samp><kbd class=pp>urlencode(data)</kbd> <span class=u>③</span></a>
|
||||
<samp>'status=test+update+from+python+3'</samp></pre>
|
||||
<ol>
|
||||
<li>Python comes with a utility function to <abbr>URL</abbr>-encode a dictionary: <code>urllib.parse.urlencode()</code>.
|
||||
<li>This is the sort of dictionary that the Identi.ca <abbr>API</abbr> is looking for. It contains one key, <code>status</code>, whose value is the text of a single status update.
|
||||
<li>This is what the <abbr>URL</abbr>-encoded string looks like. This is the <i>payload</i> that will be sent “on the wire” to the Identi.ca <abbr>API</abbr> server in your <abbr>HTTP</abbr> <code>POST</code> request.
|
||||
</ol>
|
||||
|
||||
<p>
|
||||
|
||||
<pre class=screen>
|
||||
<samp class=p>>>> </samp><kbd class=pp>from urllib.parse import urlencode</kbd>
|
||||
<samp class=p>>>> </samp><kbd class=pp>import httplib2</kbd>
|
||||
<samp class=p>>>> </samp><kbd class=pp>httplib2.debuglevel = 1</kbd>
|
||||
<samp class=p>>>> </samp><kbd class=pp>h = httplib2.Http('.cache')</kbd>
|
||||
<samp class=p>>>> </samp><kbd class=pp>data = {'status': 'Test update from Python 3'}</kbd>
|
||||
<samp class=p>>>> </samp><kbd class=pp>h.add_credentials('diveintomark', '<var>MY_SECRET_PASSWORD</var>')</kbd>
|
||||
<samp class=p>>>> </samp><kbd class=pp>resp, content = h.request('http://twitter.com/statuses/update.xml', 'POST', urlencode(data))</kbd>
|
||||
<samp class=p>>>> </samp><kbd class=pp>resp.status</kbd>
|
||||
<samp class=pp>200</samp>
|
||||
<samp class=p>>>> </samp><kbd class=pp>from xml.etree import ElementTree as etree</kbd>
|
||||
<samp class=p>>>> </samp><kbd class=pp>tree = etree.fromstring(content)</kbd>
|
||||
<samp class=p>>>> </samp><kbd class=pp>print(etree.tostring(tree))</kbd>
|
||||
<samp class=pp><status>
|
||||
<created_at>Sat May 30 19:11:38 +0000 2009</created_at>
|
||||
<id>1973974228</id>
|
||||
<text>Test update from Python 3</text>
|
||||
<source>web</source>
|
||||
<truncated>false</truncated>
|
||||
<in_reply_to_status_id />
|
||||
<in_reply_to_user_id />
|
||||
<favorited>false</favorited>
|
||||
<in_reply_to_screen_name />
|
||||
<user>
|
||||
<id>8294212</id>
|
||||
<name>Mark Pilgrim</name>
|
||||
<screen_name>diveintomark</screen_name>
|
||||
<location>Apex, NC</location>
|
||||
<description>Like a fine spice</description>
|
||||
<profile_image_url>http://s3.amazonaws.com/twitter_production/profile_images/72859681/beau_normal.jpg</profile_image_url>
|
||||
<a><samp class=p>>>> </samp><kbd class=pp>h.add_credentials('diveintomark', '<var>MY_SECRET_PASSWORD</var>')</kbd> <span class=u>①</span></a>
|
||||
<a><samp class=p>>>> </samp><kbd class=pp>resp, content = h.request('https://identi.ca/api/statuses/update.xml',</kbd> <span class=u>②</span></a>
|
||||
<a><samp class=p>... </samp><kbd class=pp> 'POST',</kbd> <span class=u>③</span></a>
|
||||
<a><samp class=p>... </samp><kbd class=pp> urlencode(data),</kbd> <span class=u>④</span></a>
|
||||
<a><samp class=p>... </samp><kbd class=pp> headers={'Content-Type': 'application/x-www-form-urlencoded'})</kbd> <span class=u>⑤</span></a></pre>
|
||||
<ol>
|
||||
<li>This is how <code>httplib2</code> handles authentication. Store your username and password with the <code>add_credentials()</code> method. When <code>httplib2</code> tries to issue the request, the server will respond with a <code>401 Authentication Required</code>, and it will list which authentication methods it supports. <code>httplib2</code> will automatically format the appropriate FIXME
|
||||
<li>
|
||||
<li>
|
||||
<li>
|
||||
<li>
|
||||
</ol>
|
||||
|
||||
<url>http://diveintomark.org/</url>
|
||||
<protected>false</protected>
|
||||
<followers_count>2565</followers_count>
|
||||
<profile_background_color>FFFFFF</profile_background_color>
|
||||
<profile_text_color>333333</profile_text_color>
|
||||
<profile_link_color>333333</profile_link_color>
|
||||
<profile_sidebar_fill_color>ffffff</profile_sidebar_fill_color>
|
||||
<profile_sidebar_border_color>333333</profile_sidebar_border_color>
|
||||
<friends_count>44</friends_count>
|
||||
<created_at>Sun Aug 19 23:58:36 +0000 2007</created_at>
|
||||
<favourites_count>71</favourites_count>
|
||||
<utc_offset>-18000</utc_offset>
|
||||
<time_zone>Eastern Time (US & Canada)</time_zone>
|
||||
<profile_background_image_url>http://static.twitter.com/images/themes/theme1/bg.gif</profile_background_image_url>
|
||||
<profile_background_tile>false</profile_background_tile>
|
||||
<statuses_count>527</statuses_count>
|
||||
<notifications>false</notifications>
|
||||
<following>false</following>
|
||||
</user>
|
||||
<pre class=screen>
|
||||
# continued from the previous example
|
||||
<samp>send: b'POST /api/statuses/update.xml HTTP/1.1
|
||||
Host: identi.ca
|
||||
Accept-Encoding: identity
|
||||
Content-Length: 32
|
||||
content-type: application/x-www-form-urlencoded
|
||||
user-agent: Python-httplib2/$Rev: 259 $
|
||||
|
||||
status=Test+update+from+Python+3'
|
||||
reply: 'HTTP/1.1 401 Unauthorized'
|
||||
send: b'POST /api/statuses/update.xml HTTP/1.1
|
||||
Host: identi.ca
|
||||
Accept-Encoding: identity
|
||||
Content-Length: 32
|
||||
content-type: application/x-www-form-urlencoded
|
||||
authorization: Basic SECRET_HASH_CONSTRUCTED_BY_HTTPLIB2
|
||||
user-agent: Python-httplib2/$Rev: 259 $
|
||||
|
||||
status=Test+update+from+Python+3'
|
||||
reply: 'HTTP/1.1 200 OK'</samp></pre>
|
||||
<ol>
|
||||
<li>FIXME
|
||||
</ol>
|
||||
|
||||
<pre class=screen>
|
||||
# continued from the previous example
|
||||
<samp class=p>>>> </samp><kbd class=pp>print(content.decode('utf-8'))</kbd>
|
||||
<samp class=pp><?xml version="1.0" encoding="UTF-8"?>
|
||||
<status>
|
||||
<text>Test update from Python 3</text>
|
||||
<truncated>false</truncated>
|
||||
<created_at>Wed Jun 10 03:53:46 +0000 2009</created_at>
|
||||
<in_reply_to_status_id></in_reply_to_status_id>
|
||||
<source>api</source>
|
||||
<id>5131472</id>
|
||||
<in_reply_to_user_id></in_reply_to_user_id>
|
||||
<in_reply_to_screen_name></in_reply_to_screen_name>
|
||||
<favorited>false</favorited>
|
||||
<user>
|
||||
<id>3212</id>
|
||||
<name>Mark Pilgrim</name>
|
||||
<screen_name>diveintomark</screen_name>
|
||||
<location>27502, US</location>
|
||||
<description>tech writer, husband, father</description>
|
||||
<profile_image_url>http://avatar.identi.ca/3212-48-20081216000626.png</profile_image_url>
|
||||
<url>http://diveintomark.org/</url>
|
||||
<protected>false</protected>
|
||||
<followers_count>329</followers_count>
|
||||
<profile_background_color></profile_background_color>
|
||||
<profile_text_color></profile_text_color>
|
||||
<profile_link_color></profile_link_color>
|
||||
<profile_sidebar_fill_color></profile_sidebar_fill_color>
|
||||
<profile_sidebar_border_color></profile_sidebar_border_color>
|
||||
<friends_count>2</friends_count>
|
||||
<created_at>Wed Jul 02 22:03:58 +0000 2008</created_at>
|
||||
<favourites_count>30768</favourites_count>
|
||||
<utc_offset>0</utc_offset>
|
||||
<time_zone>UTC</time_zone>
|
||||
<profile_background_image_url></profile_background_image_url>
|
||||
<profile_background_tile>false</profile_background_tile>
|
||||
<statuses_count>122</statuses_count>
|
||||
<following>false</following>
|
||||
<notifications>false</notifications>
|
||||
</user>
|
||||
</status></samp></pre>
|
||||
<ol>
|
||||
<li>FIXME
|
||||
</ol>
|
||||
|
||||
<p>FIXME
|
||||
|
||||
<p class=c><img class=fr src=i/identica-screenshot.png alt="screenshot of identi.ca/notice/5131472 showing status just published" width=740 height=449>
|
||||
|
||||
<p class=a>⁂
|
||||
|
||||
<h2 id=beyond-post>Beyond HTTP POST</h2>
|
||||
@@ -699,11 +778,32 @@ user-agent: Python-httplib2/$Rev: 259 $'
|
||||
|
||||
<pre class=screen>
|
||||
# continued from the previous example
|
||||
<samp class=p>>>> </samp><kbd class=pp>tree.findtext('id')</kbd>
|
||||
<samp class=pp>'1973974228'</samp>
|
||||
<samp class=p>>>> </samp><kbd class=pp>resp, delete_content = h.request('http://twitter.com/statuses/destroy/{0}.xml'.format(tree.findtext('id')), 'DELETE')</kbd>
|
||||
<samp class=p>>>> </samp><kbd class=pp>from xml.etree import ElementTree as etree</kbd>
|
||||
<samp class=p>>>> </samp><kbd class=pp>tree = etree.fromstring(content)</kbd>
|
||||
<samp class=p>>>> </samp><kbd class=pp>status_id = tree.findtext('id')</kbd>
|
||||
<samp class=p>>>> </samp><kbd class=pp>status_id</kbd>
|
||||
<samp class=pp>'5131472'</samp>
|
||||
<samp class=p>>>> </samp><kbd class=pp>resp, deleted_content = h.request('https://identi.ca/api/statuses/destroy/{0}.xml'.format(status_id), 'DELETE')</kbd>
|
||||
<samp>send: b'DELETE /api/statuses/destroy/5131472.xml HTTP/1.1
|
||||
Host: identi.ca
|
||||
Accept-Encoding: identity
|
||||
user-agent: Python-httplib2/$Rev: 259 $
|
||||
|
||||
'
|
||||
reply: 'HTTP/1.1 401 Unauthorized'
|
||||
send: b'DELETE /api/statuses/destroy/5131472.xml HTTP/1.1
|
||||
Host: identi.ca
|
||||
Accept-Encoding: identity
|
||||
authorization: Basic SECRET_HASH_CONSTRUCTED_BY_HTTPLIB2
|
||||
user-agent: Python-httplib2/$Rev: 259 $
|
||||
|
||||
'
|
||||
reply: 'HTTP/1.1 200 OK'</samp>
|
||||
<samp class=p>>>> </samp><kbd class=pp>resp.status</kbd>
|
||||
<samp class=pp>200</samp></pre>
|
||||
<ol>
|
||||
<li>FIXME
|
||||
</ol>
|
||||
|
||||
<p class=a>⁂
|
||||
|
||||
|
||||
Reference in New Issue
Block a user