{"id":2530,"date":"2025-10-05T21:03:23","date_gmt":"2025-10-05T13:03:23","guid":{"rendered":"http:\/\/blue.yn.cn\/?p=2530"},"modified":"2026-01-26T09:38:11","modified_gmt":"2026-01-26T01:38:11","slug":"networked-programs","status":"publish","type":"post","link":"http:\/\/blue.yn.cn\/?p=2530","title":{"rendered":"12. Networked Programs"},"content":{"rendered":"<h3>Networked programs<\/h3>\n<p>While many of the examples in this book have focused on reading files and looking for data in those files, there are many different sources of information when one considers the Internet.<\/p>\n<p>In this chapter we will pretend to be a web browser and retrieve web pages using the Hypertext Transfer Protocol (HTTP). Then we will read through the web page data and parse it.<\/p>\n<h3>Hypertext Transfer Protocol<\/h3>\n<p>The network protocol that powers the web is actually quite simple and there is built-in support in Python called <code>socket<\/code> which makes it very easy to make network connections and retrive data over those sockets in a Python program.<\/p>\n<p>A socket is much like a file, except that a single socket provides a two-way connection between two programs. You can both read form and write to the same socket. If youu write something to a socket, it is sent to the application at the other end of the socket. If you read from the socket, you are given the data which the other application has sent.<\/p>\n<p>But if you try to read a socket when the program on the other end of the socket has not sent any data, you just sit and wait. If the programs on both ends of the socket simply wait for some data without sending anything, the will wait for a very long time, so an important part of programs that communicate over the Internet is to have some sort of protocol.<\/p>\n<p>A protocol is a set of precise rules that determine who is to go first, what they are to do, and then what the responses are to that message, and who sends next, and so on. In a sense the two applications at either end of the socket are doing a dance and making sure not to step on each other's toes.<\/p>\n<p>There are many documents that describe these network protocols. The Hypertext Transfer Protocol is described in the following document:<\/p>\n<p><a href=\"https:\/\/www.w3.org\/Protocols\/rfc2616\/rfc2616.txt\">https:\/\/www.w3.org\/Protocols\/rfc2616\/rfc2616.txt<\/a><\/p>\n<p>This is a long and complex 176-page document with a lot of detail. If you find it interesting, feel free to read it all. But if you take a look around page 36 of RFC2616 you will find the syntax for the GET request. To request a document from a web server, we make a connection, e.g. to the <code>www.pr4e.org<\/code> server on port 80, and then send a line of the form<\/p>\n<pre><code class=\"language-python\">GET http:\/\/data.pr4e.org\/romeo.txt HTTP\/1.0<\/code><\/pre>\n<p>Where the second parameter is the web page we are requesting, and then we also send a blank line. The web server will respond with some header information about the document and a blank line followed by the document content.<\/p>\n<h3>The world's simplest web browser<\/h3>\n<p>Perhaps the easiest way to show how the HTTP protocol works is to write a very simple Python program that makes a connection to a web server and follows the rules of the HTTP protocol to request a document and display what the server sends back.<\/p>\n<pre><code class=\"language-python\">import socket\n\nmysock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\nmysock.connect((&#039;data.pr4e.org&#039;, 80))\ncmd = &#039;GET http:\/\/data.pr4e.org\/romeo.txt HTTP\/1.0\\r\\n\\r\\n&#039;.encode()\nmysock.send(cmd)\n\nwhile True:\n    data = mysock.recv(512)\n    if len(data) &lt; 1:\n        break\n    print(data.decode(),end=&#039;&#039;)\n\nmysock.close()<\/code><\/pre>\n<pre><code class=\"language-python\">import socket\n\nmysock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\nmysock.connect((&#039;blue.yn.cn&#039;, 80))\ncmd = &#039;GET http:\/\/blue.yn.cn\/download\/hello.txt HTTP\/1.0\\r\\n\\r\\n&#039;.encode()\nmysock.send(cmd)\n\nwhile True:\n    data = mysock.recv(512)\n    if len(data) &lt; 1:\n        break\n    print(data.decode(),end=&#039;&#039;)\n\nmysock.close()<\/code><\/pre>\n<p>First the program makes a connection to port 80 on the server <a href=\"http:\/\/www.pr4e.com\/\">http:\/\/www.pr4e.com\/<\/a>. Since our program is playing the role of the &quot;web browser&quot;, the HTTP protocol says we must send the GET command followed by a blank line. <code>\\r\\n<\/code> signifies an EOL (end of line), so <code>\\r\\n\\r\\n<\/code> signifies nothing between two EOL sequences. That is the equivalent of a blank line.<\/p>\n<p><img decoding=\"async\" src=\"\/wp-content\/uploads\/2025\/10\/image-1759671193811.png\" alt=\"file\" \/><\/p>\n<p>A Socket Connection<br \/>\nOnce we send that blank line, we write a loop that receives data in 512-character chunks from the socket and prints the data out until there is no more data to read (i.e., the recv() returns an empty string).<\/p>\n<p>The program produces the following output:<\/p>\n<pre><code class=\"language-python\">HTTP\/1.1 200 OK\nDate: Wed, 11 Apr 2018 18:52:55 GMT\nServer: Apache\/2.4.7 (Ubuntu)\nLast-Modified: Sat, 13 May 2017 11:22:22 GMT\nETag: &quot;a7-54f6609245537&quot;\nAccept-Ranges: bytes\nContent-Length: 167\nCache-Control: max-age=0, no-cache, no-store, must-revalidate\nPragma: no-cache\nExpires: Wed, 11 Jan 1984 05:00:00 GMT\nConnection: close\nContent-Type: text\/plain\n\nBut soft what light through yonder window breaks\nIt is the east and Juliet is the sun\nArise fair sun and kill the envious moon\nWho is already sick and pale with grief<\/code><\/pre>\n<p>The output starts with headers which the web server sends to describe the document. For example, the <code>Content-Type<\/code> header indicates that the document is a plain text document (<code>text\/plain<\/code>).<\/p>\n<p>After the server sends us the headers, it adds a blank line to indicate the end of the headers, and then sends the actual data of the file romeo.txt.<\/p>\n<p>This example shows how to make a low-level network connection with sockets. Sockets can be used to communicate with a web server or with a mail server or many other kinds of servers. All that is needed is to find the document which describes the protocol and write the code to send and receive the data according to the protocol.<\/p>\n<p>However, since the protocol that we use most commonly is the HTTP web protocol, Python has a special library specifically designed to support the HTTP protocol for the retrieval of documents and data over the web.<\/p>\n<p>One of the requiments for using the HTTP protocol is the need to send and receive data as bytes objects, instead of strings. In the preceding example, the <code>encode()<\/code> and <code>decode()<\/code> methods convert strings into bytes objects and back again.<\/p>\n<p>The next example uses <code>b&#039;&#039;<\/code> notation to specify that a variable should be stored as a bytes objects. <code>encode()<\/code> and <code>b&#039;&#039;<\/code> are equivalent.<\/p>\n<pre><code class=\"language-python\">>&gt;&gt; b&#039;Hello world&#039;\nb&#039;Hello world&#039;\n>&gt;&gt; &#039;Hello world&#039;.encode()\nb&#039;Hello world&#039;<\/code><\/pre>\n<h3>Retrieving an image over HTTP<\/h3>\n<p>In the above example, we retrieved a plain text file which had newlines in the file and we simply copied the data to the screen as the program ran. We can use a similar program to retrieve an image aross using HTTP. Instead of copying the data to the screen as the program runs, we accumulate the data in a string, trim off the headers, and then save the image data to a file as follows:<\/p>\n<pre><code class=\"language-python\">import socket\nimport time\n\nHOST = &#039;data.pr4e.org&#039;\nPORT = 80\nmysock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\nmysock.connect((HOST, PORT))\nmysock.sendall(b&#039;GET http:\/\/data.pr4e.org\/cover3.jpg HTTP\/1.0\\r\\n\\r\\n&#039;)\ncount = 0\npicture = b&quot;&quot;\n\nwhile True:\n    data = mysock.recv(5120)\n    if len(data) &lt; 1: break\n    #time.sleep(0.25)\n    count = count + len(data)\n    print(len(data), count)\n    picture = picture + data\n\nmysock.close()\n\n# Look for the end of the header (2 CRLF)\npos = picture.find(b&quot;\\r\\n\\r\\n&quot;)\nprint(&#039;Header length&#039;, pos)\nprint(picture[:pos].decode())\n\n# Skip past the header and save the picture data\npicture = picture[pos+4:]\nfhand = open(&quot;stuff.jpg&quot;, &quot;wb&quot;)\nfhand.write(picture)\nfhand.close()<\/code><\/pre>\n<p>When the program runs, it produces the following output:<\/p>\n<pre><code class=\"language-python\">2824 2824\n2824 5648\n5120 10768\n132 124584\n...\n5120 209332\n5120 214452\n5120 219572\n3524 223096\n2824 225920\n4688 230608\nHeader length 394\nHTTP\/1.1 200 OK\nDate: Mon, 06 Oct 2025 02:09:16 GMT\nServer: Apache\/2.4.52 (Ubuntu)\nLast-Modified: Mon, 15 May 2017 12:27:40 GMT\nETag: &quot;38342-54f8f2e5b6277&quot;\nAccept-Ranges: bytes\nContent-Length: 230210\nVary: Accept-Encoding\nCache-Control: max-age=0, no-cache, no-store, must-revalidate\nPragma: no-cache\nExpires: Wed, 11 Jan 1984 05:00:00 GMT\nConnection: close\nContent-Type: image\/jpeg<\/code><\/pre>\n<p>You can see that for this url, the <code>Content-Type<\/code> header indicates that body of the document is an image (<code>image\/jpeg<\/code>). Once the program completes, you can view the image data by opening the file <code>stuff.jpg<\/code> in an image viewer.<\/p>\n<p>As the program runs, you can see that we don't get 5120 characters each time we call the <code>recv()<\/code> method. We get as many characters as have been transferred across the network to us by the web server at the moment we call <code>recv()<\/code>. In this example, we either get as few as 132 characters each time we request up to 5120 characters of data.<\/p>\n<p>Your results may be different depending on your network speed. Also note that on the last call to <code>recv()<\/code> we get 4688 bytes, which is the end of the stream, and in the next call to <code>recv()<\/code> we get a zero-length string that tells us that the server has called <code>close()<\/code> on its end of the socket and there is no more data fothcoming.<\/p>\n<p>We can slow down our successive <code>recv()<\/code> call s by uncommenting the call to <code>time.sleep()<\/code>. This way, we wait a quarter of a second after each call so that the server can &quot;get ahead&quot; of us and send more data to us before we call <code>recv()<\/code> again. With the delay, in place the program executes as follows:<\/p>\n<pre><code class=\"language-python\">4236 4236\n5120 9356\n5120 14476\n...\n5120 24716\n5120 229516\n1092 230608\nHeader length 394\nHTTP\/1.1 200 OK\nDate: Mon, 06 Oct 2025 02:28:02 GMT\nServer: Apache\/2.4.52 (Ubuntu)\nLast-Modified: Mon, 15 May 2017 12:27:40 GMT\nETag: &quot;38342-54f8f2e5b6277&quot;\nAccept-Ranges: bytes\nContent-Length: 230210\nVary: Accept-Encoding\nCache-Control: max-age=0, no-cache, no-store, must-revalidate\nPragma: no-cache\nExpires: Wed, 11 Jan 1984 05:00:00 GMT\nConnection: close\nContent-Type: image\/jpeg<\/code><\/pre>\n<p>Now other than the first and last calls to <code>recv()<\/code>, we now get 5120 characters each time we ask for new data.<\/p>\n<p>There is a buffer between the server making <code>send()<\/code> and our application making <code>recv()<\/code> requests. When we run the program with the delay in place, at some point the server might fill up the buffer in the socket and be forced to pause until our program starts to empty the buffer. The pausing of either the sending application or the receiving application is called &quot;flow control&quot;.<\/p>\n<h3>Retrieving web pages with <code>urllib<\/code><\/h3>\n<p>While we can manually send and receive data over HTTP using the socket library, there is a much simpler way to perform this common task in Python by using the <code>urllib<\/code> library.<\/p>\n<p>Using <code>urllib<\/code>, you can treat a web page much like a file. You simply indicate which web page you would like to retrieve and <code>urllib<\/code> handles all of the HTTP protocol and header details.<\/p>\n<p>The equivalent code to read the romeo.txt file from the web using <code>urllib<\/code> is as follows:<\/p>\n<pre><code class=\"language-python\">import urllib.request\n\nfhand = urllib.request.urlopen(&#039;http:\/\/data.pr4e.org\/romeo.txt&#039;)\nfor line in fhand:\n    print(line.decode().strip())<\/code><\/pre>\n<p>Once the web page has been opened with <code>urllib.requests.urlopen<\/code>, we can treat it like a file and read through it using a <code>for<\/code> loop.<\/p>\n<p>When the program runs, we only see the output of the contens of the file. The headers are still sent, but the <code>urllib<\/code> code consumes the headers and only returns the data to us.<\/p>\n<pre><code class=\"language-python\">But soft what light through yonder window breaks\nIt is the east and Juliet is the sun\nArise fair sun and kill the envious moon\nWho is already sick and pale with grief<\/code><\/pre>\n<p>As an example, we can write a program to retrieve the data for <code>romeo.txt<\/code> and compute the frequency of each word in the file as follows:<\/p>\n<pre><code class=\"language-python\">import urllib.request, urllib.parse, urllib.error\n\nfhand = urllib.request.urlopen(&#039;http:\/\/data.pr4e.org\/romeo.txt&#039;)\n\ncounts = dict()\nfor line in fhand:\n    words = line.decode().split()\n    for word in words:\n        counts[word] = counts.get(word, 0) + 1\nprint(counts)<\/code><\/pre>\n<p>Again, once we have opened the web page, we can read it like a local file.<\/p>\n<h3>Reading binary files using <code>urllib<\/code><\/h3>\n<p>Sometimes you want to retrieve a non-text (or binary) file such as an image or vidio file. The data in these files is generally not useful to print out, but you can easily make a copy of a URL to a local file on your hard disk using <code>urllib<\/code>.<\/p>\n<p>The pattern is to open the URL and use <code>read<\/code> to download the entire contents of the document into a string variable (<code>img<\/code>) then write that information to a local file as follows:<\/p>\n<pre><code class=\"language-python\">import urllib.request, urllib.parse, urllib.error\n\nimg = urllib.request.urlopen(&#039;http:\/\/data.pr4e.org\/cover3.jpg&#039;).read()\nfhand = open(&#039;cover3.jpg&#039;, &#039;wb&#039;)\nfhand.write(img)\nfhand.close()<\/code><\/pre>\n<p>This program reads all of the data in at once across the network and stores it in the variable <code>img<\/code> in the main memory of your computer, then opens the file <code>cover.jpg<\/code> and writes the data out to your disk. The <code>wb<\/code> argument for <code>open()<\/code> opens a binary file for writing only. This program will work if the size of the file is less than the size of the memory of your computer.<\/p>\n<p>However if this is a large audio or video file, this program may crash or at least run extremely slowly when your computer runs out of memory. In order to avoid running out of memory, we retrieve the data in blocks (or buffers) and then write each block to your disk before retrieving the next block. This way the program can read any size file without using up all of the memory you have in your computer.<\/p>\n<pre><code class=\"language-python\">import urllib.request, urllib.parse, urllib.error\n\nimg = urllib.request.urlopen(&#039;http:\/\/data.pr4e.org\/cover3.jpg&#039;)\nfhand = open(&#039;cover3.jpg&#039;, &#039;wb&#039;)\nsize = 0\nwhile True:\n    info = img.read(100000)\n    if len(info) &lt; 1: break\n    size = size + len(info)\n    fhand.write(info)\n\nprint(size, &#039;characters copied.&#039;)\nfhand.close()<\/code><\/pre>\n<p>In this example, we read only 100,000 characters at a time and then write those characters to the <code>cover3.jpg<\/code> file before retrieving the next 100,000 characters of data from the web.<\/p>\n<p>This program runs as follows:<\/p>\n<pre><code class=\"language-python\">230210 characters copied.<\/code><\/pre>\n<h3>Parsing HTML and scraping the web<\/h3>\n<p>One of the common uses of the <code>urllib<\/code> capability in Python is to scrape the web. Web scraping is when we write a program that pretends to be a web browser and retrieves pages, then examines the data in those pages looking for patterns.<\/p>\n<p>As an example, a search engine such as Google will look at the source of one web page and extract the links to other pages and retrieve those pages, extracting links, and so on. Using this technique, Google spiders its way through nearly all of the pages on the web.<\/p>\n<p>Google also uses the frequency of links from pages it finds to a particular page as one measure of how &quot;important&quot; a page is and how high the page should appear in its search results.<\/p>\n<h3>Parsing HTML using regular expressions<\/h3>\n<p>One simple way to parse HTML is to use regular expressions to repeately search for and extract substrings that match a paticular pattern.<\/p>\n<p>Here is a simple web page:<\/p>\n<pre><code class=\"language-python\">&lt;h1&gt;The First Page&lt;\/h1&gt;\n&lt;p&gt;\nIf you like, you can switch to the\n&lt;a href=&quot;http:\/\/www.dr-chuck.com\/page2.htm&quot;&gt;\nSecond Page&lt;\/a&gt;.\n&lt;\/p&gt;<\/code><\/pre>\n<p>We can construct a well-formed regular expression to match and extract the link values from the above text as follows:<\/p>\n<pre><code class=\"language-python\">href=&quot;http[s]?:\/\/.+?&quot;<\/code><\/pre>\n<p>Our regular expression looks for strings that start with &quot;href=&quot;http:\/\/&quot; or &quot;href=&quot;https:\/\/&quot;, followed by one or more characters (<code>.+?<\/code>), followed by another dobule quote. The question mark behind the <code>[s]?<\/code> indicates to search for the string &quot;http&quot; followed by zero or one &quot;s&quot;.<\/p>\n<p>The question mark added to the <code>.+?<\/code> indicates that the match is to be done in a &quot;non-greedy&quot; fashion instead of a &quot;greedy&quot; fashion. A non-greedy match tries to find the smallest possible matching string and a greedy match tries to find the largest possible matching string.<\/p>\n<p>We add parentheses to our regular expression to indicate which part of our matched string we would like to extract, and produce the following program:<\/p>\n<pre><code class=\"language-python\"># Search for link values within URL input\nimport urllib.request, urllib.parse, urllib.error\nimport re\nimport ssl\n\n# Ignore SSL certificate errors\nctx = ssl.create_default_context()\nctx.check_hostname = False\nctx.verify_mode = ssl.CERT_NONE\n\nurl = input(&#039;Enter - &#039;)\nhtml = urllib.request.urlopen(url, context=ctx).read()\nlinks = re.findall(b&#039;href=&quot;(http[s]?:\/\/.*?)&quot;&#039;, html)\nfor link in links:\n    print(link.decode())<\/code><\/pre>\n<p>The <code>ssl<\/code> library allows this program to access web sites that strictly enforce HTTPS. The <code>read<\/code> method returns HTML source code as a bytes object instead of returning an HTTPResponse object. The <code>findall<\/code> regular expression method will give us a list of all of the strings that match our regular expression, returning only the link text between the double quotes.<\/p>\n<p>When we run the program and input a URL, we get the following output:<\/p>\n<pre><code class=\"language-python\">Enter - https:\/\/docs.python.org\nhttps:\/\/docs.python.org\/3\/index.html\nhttps:\/\/www.python.org\/\nhttps:\/\/docs.python.org\/3.8\/\nhttps:\/\/docs.python.org\/3.7\/\nhttps:\/\/docs.python.org\/3.5\/\nhttps:\/\/docs.python.org\/2.7\/\nhttps:\/\/www.python.org\/doc\/versions\/\nhttps:\/\/www.python.org\/dev\/peps\/\nhttps:\/\/wiki.python.org\/moin\/BeginnersGuide\nhttps:\/\/wiki.python.org\/moin\/PythonBooks\nhttps:\/\/www.python.org\/doc\/av\/\nhttps:\/\/www.python.org\/\nhttps:\/\/www.python.org\/psf\/donations\/\nhttp:\/\/sphinx.pocoo.org\/<\/code><\/pre>\n<p>Regular expressions work very nicely when your HTML is well formatted and predictable. But since there are a lot of &quot;broken&quot; HTML pages out there, a solution only using regular expressions might either miss some valid links or end up with bad data.<\/p>\n<p>This can be solved by using a robust HTML parsing library.<\/p>\n<h3>Parsing HTML using BeautifulSoup<\/h3>\n<p>Even though HTML looks likes XML and some pages are carefull constructed to be XML, most HTML is generally broken in ways that cause an XML parser to reject the entire page of HTML as improperly formed.<\/p>\n<p>There are a number of Python libraries which can help you parse HTML and extract data from the pages. Each of the libraries has its strengths and weaknesses and you can pick one based on your needs.<\/p>\n<p>As an example, we will simply parse some HTML input and extract links using the BeautifulSoup library. BeatifulSoup tolerates highly flawed HTML and still lets you easily extract the data you need. You can download and install the BeautifulSoup code from:<\/p>\n<p><a href=\"https:\/\/pypi.python.org\/pypi\/beautifulsoup4\">https:\/\/pypi.python.org\/pypi\/beautifulsoup4<\/a><\/p>\n<p>Information on installing BeautifulSoup with the Python Package Index tool <code>pip<\/code> is available at:<\/p>\n<p><a href=\"https:\/\/packaging.python.org\/tutorials\/installing-packages\/\">https:\/\/packaging.python.org\/tutorials\/installing-packages\/<\/a><\/p>\n<p><code>pip install beautifulsoup4<\/code><\/p>\n<p>We will use <code>urllib<\/code> to read the page and then use <code>BeautifulSoup<\/code> to extract the <code>href<\/code> attributes from the anchor (<code>a<\/code>) tags.<\/p>\n<pre><code class=\"language-python\"># To run this, download the BeautifulSoup zip file\n# http:\/\/www.py4e.com\/code3\/bs4.zip\n# or pip install beautifulsoup4 to ensure you have the latest version\n# and unzip it in the same directory as this file\n\nimport urllib.request, urllib.parse, urllib.error\nfrom bs4 import BeautifulSoup\nimport ssl # defauts to certicate verification and most secure protocol (now TLS)\n\n# Ignore SSL\/TLS certificate errors\nctx = ssl.create_default_context()\nctx.check_hostname = False\nctx.verify_mode = ssl.CERT_NONE\n\nurl = input(&#039;Enter - &#039;)\nhtml = urllib.request.urlopen(url, context=ctx).read()\nsoup = BeautifulSoup(html, &#039;html.parser&#039;)\n\n# Retrieve all of the anchor tags\ntags = soup(&#039;a&#039;)\nfor tag in tags:\n    print(tag.get(&#039;href&#039;, None))<\/code><\/pre>\n<p>The program prompts for a web address, then opens the web page, reads the data and passes the data to the BeautifulSoup parser, and then retrieves all of the anchor tags and prints out the <code>href<\/code> attribute for each tag.<\/p>\n<p>When the program runs, it produces the following output:<\/p>\n<pre><code class=\"language-python\">Enter - https:\/\/docs.python.org\ngenindex.html\npy-modindex.html\nhttps:\/\/www.python.org\/\n#\nwhatsnew\/3.6.html\nwhatsnew\/index.html\ntutorial\/index.html\nlibrary\/index.html\nreference\/index.html\nusing\/index.html\nhowto\/index.html\ninstalling\/index.html\ndistributing\/index.html\nextending\/index.html\nc-api\/index.html\nfaq\/index.html\npy-modindex.html\ngenindex.html\nglossary.html\nsearch.html\ncontents.html\nbugs.html\nabout.html\nlicense.html\ncopyright.html\ndownload.html\nhttps:\/\/docs.python.org\/3.8\/\nhttps:\/\/docs.python.org\/3.7\/\nhttps:\/\/docs.python.org\/3.5\/\nhttps:\/\/docs.python.org\/2.7\/\nhttps:\/\/www.python.org\/doc\/versions\/\nhttps:\/\/www.python.org\/dev\/peps\/\nhttps:\/\/wiki.python.org\/moin\/BeginnersGuide\nhttps:\/\/wiki.python.org\/moin\/PythonBooks\nhttps:\/\/www.python.org\/doc\/av\/\ngenindex.html\npy-modindex.html\nhttps:\/\/www.python.org\/\n#\ncopyright.html\nhttps:\/\/www.python.org\/psf\/donations\/\nbugs.html\nhttp:\/\/sphinx.pocoo.org\/<\/code><\/pre>\n<p>This list is much longer because some HTML anchor tags are relative paths (e.g., tutorial\/index.html) or in-page reference (e.g., '#') that do not include &quot;http:\/\/&quot; or &quot;https:\/\/&quot;, which was a requirement in our regular expression.<\/p>\n<p>You can use also BeautifulSoup to pull out various parts of each tag:<\/p>\n<pre><code class=\"language-python\"># To run this, download the BeautifulSoup zip file\n# http:\/\/www.py4e.com\/code3\/bs4.zip\n# and unzip it in the same directory as this file\n\nfrom urllib.request import urlopen\nfrom bs4 import BeautifulSoup\nimport ssl\n\n# Ignore SSL certificate errors\nctx = ssl.create_default_context()\nctx.check_hostname = False\nctx.verify_mode = ssl.CERT_NONE\n\nurl = input(&#039;Enter - &#039;)\nhtml = urlopen(url, context=ctx).read()\nsoup = BeautifulSoup(html, &quot;html.parser&quot;)\n\n# Retrieve all of the anchor tags\ntags = soup(&#039;a&#039;)\nfor tag in tags:\n    # Look at the parts of a tag\n    print(&#039;TAG:&#039;, tag)\n    print(&#039;URL:&#039;, tag.get(&#039;href&#039;, None))\n    print(&#039;Contents:&#039;, tag.contents[0])\n    print(&#039;Attrs:&#039;, tag.attrs)<\/code><\/pre>\n<pre><code class=\"language-python\">Enter - http:\/\/www.dr-chuck.com\/page1.htm\nTAG: &lt;a href=&quot;http:\/\/www.dr-chuck.com\/page2.htm&quot;&gt;\nSecond Page&lt;\/a&gt;\nURL: http:\/\/www.dr-chuck.com\/page2.htm\nContent: [&#039;\\nSecond Page&#039;]\nAttrs: [(&#039;href&#039;, &#039;http:\/\/www.dr-chuck.com\/page2.htm&#039;)]<\/code><\/pre>\n<p><code>html.parser<\/code> is the HTML parser included in the standard Python 3 library. Information on other HTML parsers is available at:<\/p>\n<p><a href=\"http:\/\/www.crummy.com\/software\/BeautifulSoup\/bs4\/doc\/#installing-a-parser\">http:\/\/www.crummy.com\/software\/BeautifulSoup\/bs4\/doc\/#installing-a-parser<\/a><\/p>\n<p>These examples only begin to show the power of BeautifulSoup when it comes to parsing HTML.<\/p>\n<h3>Bonus section for Unix \/ Linux users<\/h3>\n<p>If you have a Linux, Unix, or Macintosh computer, you probably have commands built in to your operating system that retrieves both plain text and binary files using the HTTP or File Transfer (FTP) protols. One of these commands is <code>curl<\/code>:<\/p>\n<p><code>$ curl -O http:\/\/www.py4e.com\/cover.jpg<\/code><\/p>\n<p>The command <code>curl<\/code> is short for &quot;copy URL&quot; and so the two examples listed earlier to retrieve binary files with <code>urllib<\/code> are cleverly named <code>curl1.py<\/code> and <code>curl2.py<\/code> on <a href=\"https:\/\/www.py4e.com\/code3\">https:\/\/www.py4e.com\/code3<\/a> as they implement similar functionnality to the <code>curl<\/code> command. There is also a <code>curl3.py<\/code> sample program that does this task a little more effectively, in case you actually want to use this pattern in a program you are writing.<\/p>\n<p>A second command that functions very similarly is <code>wget<\/code>:<\/p>\n<p><code>$ wget http:\/\/www.py4e.com\/cover.jpg<\/code><\/p>\n<p>Both of these commands make retrieving webpages and remote files a simple task.<\/p>\n<h3>Glossary<\/h3>\n<p><strong>BeautifulSoup<\/strong><br \/>\nA python library for parsing HTML documents and extracting data from HTML documents that compensates for most of the imperfections in the HTML that browsers generally ignore. You can download the BeautifulSoup code from <a href=\"http:\/\/www.crummy.com\/\">http:\/\/www.crummy.com\/<\/a><\/p>\n<p><strong>port<\/strong><br \/>\nA number that generally indicates which application you are contacting when you make a socket connection to a server. As an example, web traffic usually uses port 80 while email traffic uses port 25.<\/p>\n<p><strong>scrape<\/strong><br \/>\nWhen a program pretends to be an web browser and retrieves a web page, then looks at the web page content. Often programs are following the links in one page to find the next page so they can traverse a network of pages or a social network.<\/p>\n<p><strong>socket<\/strong><br \/>\nA network connection between two applications where the applications can send and receive data in either diretion.<\/p>\n<p><strong>spider<\/strong><br \/>\nThe act of a web search engine retrieving a page and then all the pages linked from a page and so on until they have nearly all of the pages on the Internet which they use to build their search index.<\/p>\n<h3>Exercise<\/h3>\n<p><strong>Exercise 1:<\/strong> Change the socket program <code>socket1.py<\/code> to prompt the user for the URL so it can read any web page.<\/p>\n<p>You can use <code>split(&#039;\/&#039;)<\/code> to break the URL into its component parts so you can extract the host name for the socket <code>connect<\/code> call. Add error checking using <code>try<\/code> and <code>except<\/code> to handle the condition where user enters an improperly formatted or non-existent URL.<\/p>\n<pre><code class=\"language-python\">import socket\n\n# This is using HTTP 1.0 - not all servers support the oldest protocol\n# Try http:\/\/data.pr4e.org\/romeo.txt if your server fails.\n\nurl = input(&#039;Enter: &#039;)\nwords = url.split(&#039;\/&#039;)\nhost = words[2]\n\nmysock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\ntry:\n    mysock.connect((host, 80))\nexcept:\n    print(&#039;error!&#039;)\n    exit()\nmysock.send((&#039;GET &#039;+url+&#039; HTTP\/1.0\\r\\n\\r\\n&#039;).encode())\n\nwhile True:\n    data = mysock.recv(512)\n    if (len(data) &lt; 1):\n        break\n    print(data.decode(), end=&#039;&#039;)\n\nmysock.close()<\/code><\/pre>\n<p><strong>Exercise 2:<\/strong> Change your socket program so that it counts the number of character it has received and stops displaying displaying any text after it has shown 3000 characters. The program should retrieve the entire document and count the total number of characters and display the count of the number of characters at the end of the document.<\/p>\n<pre><code class=\"language-python\">import socket\n\nurl = input(&#039;Enter: &#039;)\nwords = url.split(&#039;\/&#039;)\nhost = words[2]  # \u4eceURL\u4e2d\u63d0\u53d6\u4e3b\u673a\u540d\uff08\u5982www.py4e.com\uff09\n\n# \u521b\u5efaTCP socket\nmysock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\ntry:\n    mysock.connect((host, 80))  # \u8fde\u63a5HTTP\u9ed8\u8ba4\u7aef\u53e380\nexcept Exception as e:\n    print(f&#039;Connection error: {e}&#039;)  # \u66f4\u8be6\u7ec6\u7684\u9519\u8bef\u63d0\u793a\n    exit()\n\n# \u53d1\u9001HTTP GET\u8bf7\u6c42\uff08\u9700\u6309HTTP\u534f\u8bae\u683c\u5f0f\uff0c\u672b\u5c3e\u5fc5\u987b\u52a0\\r\\n\\r\\n\uff09\nrequest = f&#039;GET {url} HTTP\/1.0\\r\\n\\r\\n&#039;\nmysock.send(request.encode())  # \u5b57\u7b26\u4e32\u8f6c\u4e8c\u8fdb\u5236\u53d1\u9001\n\n# \u65b0\u589e\uff1a\u7528\u4e8e\u8ba1\u6570\u548c\u5b58\u50a8\u5185\u5bb9\u7684\u53d8\u91cf\ntotal_chars = 0  # \u8bb0\u5f55\u63a5\u6536\u7684\u603b\u5b57\u7b26\u6570\ndisplay_content = &#039;&#039;  # \u5b58\u50a8\u524d3000\u4e2a\u5b57\u7b26\uff08\u7528\u4e8e\u622a\u65ad\u663e\u793a\uff09\n\n# \u5faa\u73af\u63a5\u6536\u6570\u636e\uff08\u76f4\u5230\u670d\u52a1\u5668\u5173\u95ed\u8fde\u63a5\uff09\nwhile True:\n    data = mysock.recv(512)  # \u6bcf\u6b21\u6700\u591a\u63a5\u6536512\u5b57\u8282\n    if len(data) &lt; 1:  # \u63a5\u6536\u4e0d\u5230\u6570\u636e \u2192 \u8fde\u63a5\u5173\u95ed\uff0c\u9000\u51fa\u5faa\u73af\n        break\n\n    # 1. \u5c06\u4e8c\u8fdb\u5236\u6570\u636e\u8f6c\u5b57\u7b26\u4e32\uff08\u6309UTF-8\u89e3\u7801\uff0c\u517c\u5bb9\u5927\u591a\u6570\u7f51\u9875\uff09\n    data_str = data.decode(&#039;utf-8&#039;, errors=&#039;ignore&#039;)  # errors=&#039;ignore&#039;\u907f\u514d\u7279\u6b8a\u5b57\u7b26\u89e3\u7801\u62a5\u9519\n\n    # 2. \u7d2f\u8ba1\u603b\u5b57\u7b26\u6570\uff08\u7edf\u8ba1\u5b8c\u6574\u6587\u6863\u7684\u5b57\u7b26\u6570\uff09\n    total_chars += len(data_str)\n\n    # 3. \u6536\u96c6\u524d3000\u4e2a\u5b57\u7b26\uff08\u8d85\u8fc7\u540e\u4e0d\u518d\u6dfb\u52a0\uff09\n    if len(display_content) &lt; 3000:\n        # \u8ba1\u7b97\u8fd8\u9700\u8865\u5145\u591a\u5c11\u5b57\u7b26\u52303000\n        remaining = 3000 - len(display_content)\n        # \u82e5\u5f53\u524d\u6570\u636e\u8d85\u8fc7\u5269\u4f59\u957f\u5ea6\uff0c\u53ea\u53d6\u524dremaining\u4e2a\u5b57\u7b26\uff1b\u5426\u5219\u5168\u53d6\n        display_content += data_str[:remaining]\n\n# 4. \u663e\u793a\u524d3000\u4e2a\u5b57\u7b26\uff08\u82e5\u603b\u5b57\u7b26\u6570\u4e0d\u8db33000\uff0c\u5219\u663e\u793a\u5168\u90e8\uff09\nprint(&quot;\u524d3000\u4e2a\u5b57\u7b26\uff1a&quot;)\nprint(display_content)\n\n# 5. \u663e\u793a\u603b\u5b57\u7b26\u6570\uff08\u5b8c\u6574\u6587\u6863\u7684\u5b57\u7b26\u7edf\u8ba1\uff09\nprint(f&quot;\\n\u6587\u6863\u603b\u5b57\u7b26\u6570\uff1a{total_chars}&quot;)\n\n# \u5173\u95edsocket\u8fde\u63a5\nmysock.close()<\/code><\/pre>\n<p><code>Enter: http:\/\/data.pr4e.org\/mbox-short.txt<\/code><\/p>\n<p><strong>Exercise 3:<\/strong> Use <code>urllib<\/code> to replicate the previous exercise of (1) retrieving the document from a URL, (2) displaying up to 3000 characters, and (3) counting the overall number of characters in the document. Don't worry about the headers for this exercise, simply show the first 3000 characters of the document contents.<\/p>\n<pre><code class=\"language-python\">import urllib.request\nurl = input(&#039;Enter: &#039;)\nfhand = urllib.request.urlopen(url)\ncontent = fhand.read().decode(&#039;utf8&#039;, errors=&#039;ignore&#039;)\n\ntotal_chars = len(content)\n\nfirst_3000 = content[:3000]\nprint(&quot;\u524d 3000 \u4e2a\u5b57\u7b26\uff1a&quot;)\nprint(first_3000)\n\nprint(f&#039;\\n\u6587\u6863\u603b\u5b57\u8282\u6570\uff1a {total_chars}&#039;)<\/code><\/pre>\n<p><strong>Exercise 4:<\/strong> Change the <code>urllinks.py<\/code> program to extract and count paragraph (p) tags from the retrieved HTML document and display the count of the paragraphs as the output of your program. Do not display the paragraph text, only count them. Test your program on several small web pages as well as some large web pages.<\/p>\n<pre><code class=\"language-python\">import urllib.request, urllib.parse, urllib.error\nfrom bs4 import BeautifulSoup\nimport ssl\n\nctx = ssl.create_default_context()\nctx.check_hostname = False\nctx.verify_mode = ssl.CERT_NONE\n\nurl = input(&#039;Enter - &#039;)\nhtml = urllib.request.urlopen(url, context=ctx).read()\nsoup = BeautifulSoup(html, &#039;html.parser&#039;)\n\ntags = soup(&#039;p&#039;)\nprint(f&#039;\u6bb5\u843d\uff08p\u6807\u7b7e\uff09\u6570\u91cf\uff1a{len(tags)}&#039;)<\/code><\/pre>\n<p><strong>Exercise 5:<\/strong> (Advanced) Change the socket program so that it only shows data after the headers and a blank line have been received. Remember that <code>recv<\/code> receives characters (newlines and all), not lines.<\/p>\n<pre><code class=\"language-python\">import socket\n\nmysock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\nmysock.connect((&#039;data.pr4e.org&#039;, 80))\ncmd = &#039;GET http:\/\/data.pr4e.org\/romeo.txt HTTP\/1.0\\r\\n\\r\\n&#039;.encode()\nmysock.send(cmd)\n\nfull_response = b&#039;&#039;\nwhile True:\n    data = mysock.recv(512)\n    if len(data) &lt; 1:\n        break\n    full_response += data\n\nmysock.close()\n\n# \u5206\u79bb\u54cd\u5e94\u5934\u548c\u4e3b\u4f53\u5185\u5bb9\uff1a\u627e\u5230\u7a7a\u884c\uff08\\r\\n\\r\\n\uff09\u7684\u4f4d\u7f6e\n# \u8f6c\u6362\u4e3a\u5b57\u7b26\u4e32\u5904\u7406\uff08\u7528 &#039;utf-8&#039; \u89e3\u7801\uff0c\u4fdd\u7559\u539f\u59cb\u6362\u884c\u7b26\uff09\nresponse_str = full_response.decode(&#039;utf-8&#039;)\n# \u5b9a\u4f4d\u7a7a\u884c\u7684\u8d77\u59cb\u7d22\u5f15\uff08\u54cd\u5e94\u5934\u7ed3\u675f\u7684\u4f4d\u7f6e\uff09\nheader_end = response_str.find(&#039;\\r\\n\\r\\n&#039;)\n\nif header_end != -1:\n    body = response_str[header_end + 4:]  # +4 \u8df3\u8fc7\u7a7a\u884c\n    print(&quot;\u54cd\u5e94\u4e3b\u4f53\u5185\u5bb9\uff1a&quot;)\n    print(body)\nelse:\n    # \u82e5\u672a\u627e\u5230\u7a7a\u884c\uff08\u5f02\u5e38\u60c5\u51b5\uff09\uff0c\u76f4\u63a5\u663e\u793a\u5168\u90e8\u5185\u5bb9\n    print(&quot;\u672a\u627e\u5230\u54cd\u5e94\u5934\u5206\u9694\u7b26\uff0c\u663e\u793a\u5168\u90e8\u5185\u5bb9\uff1a&quot;)\n    print(response_str)<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Networked programs While many of the examples in this b&#8230; &raquo; <a class=\"read-more-link\" href=\"http:\/\/blue.yn.cn\/?p=2530\">\u9605\u8bfb\u5168\u6587<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[24],"tags":[],"class_list":["post-2530","post","type-post","status-publish","format-standard","hentry","category-python"],"_links":{"self":[{"href":"http:\/\/blue.yn.cn\/index.php?rest_route=\/wp\/v2\/posts\/2530","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/blue.yn.cn\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/blue.yn.cn\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/blue.yn.cn\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/blue.yn.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2530"}],"version-history":[{"count":11,"href":"http:\/\/blue.yn.cn\/index.php?rest_route=\/wp\/v2\/posts\/2530\/revisions"}],"predecessor-version":[{"id":2640,"href":"http:\/\/blue.yn.cn\/index.php?rest_route=\/wp\/v2\/posts\/2530\/revisions\/2640"}],"wp:attachment":[{"href":"http:\/\/blue.yn.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2530"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/blue.yn.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2530"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/blue.yn.cn\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2530"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}