MIL-005: wiki in English, Danish and Bahasa Malaysia #39
@@ -154,7 +154,13 @@ python -m scrape_movies hacker-news top
|
|||||||
|
|
||||||
### Lecture: Is web scraping legal and ethical?
|
### Lecture: Is web scraping legal and ethical?
|
||||||
|
|
||||||
A short summary in our own words; it is not legal advice.
|
A short summary in our own words; it is not legal advice. Reading the site's `robots.txt` comes first:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl https://news.ycombinator.com/robots.txt
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
- **Public, non-copyrighted data** is generally fine to read. Court cases such as Genius v. Google and hiQ v. LinkedIn show that the line is not simple.
|
- **Public, non-copyrighted data** is generally fine to read. Court cases such as Genius v. Google and hiQ v. LinkedIn show that the line is not simple.
|
||||||
- **Copyrighted content** (a video, a blog post) must not be copied, and **data behind a login** is covered by the site's user agreement, so do not scrape it.
|
- **Copyrighted content** (a video, a blog post) must not be copied, and **data behind a login** is covered by the site's user agreement, so do not scrape it.
|
||||||
@@ -222,7 +228,7 @@ See [LICENSE](./LICENSE).
|
|||||||
## 🔗 Links
|
## 🔗 Links
|
||||||
|
|
||||||
- [Repository][repository]
|
- [Repository][repository]
|
||||||
- [Documentation][documentation]
|
- [Documentation][documentation]: wiki in [English](https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/045-scrape-movies/wiki/Home), [Dansk](https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/045-scrape-movies/wiki/Home-da) and [Bahasa Melayu](https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/045-scrape-movies/wiki/Home-ms); the pages are in `docs/wiki/`
|
||||||
- [Issue tracker][issues]
|
- [Issue tracker][issues]
|
||||||
|
|
||||||
## 📎 Appendix: Project documents
|
## 📎 Appendix: Project documents
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
@@ -7,6 +7,7 @@ The images are written to docs/images/ and used by the README and the wiki.
|
|||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
import urllib.request
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from capture_page import capture_page
|
from capture_page import capture_page
|
||||||
@@ -36,6 +37,8 @@ STEPS: dict[str, list[str]] = {
|
|||||||
}
|
}
|
||||||
## Screenshots of a file's first lines: image name, file and number of lines.
|
## Screenshots of a file's first lines: image name, file and number of lines.
|
||||||
FILES: dict[str, tuple[str, int]] = {"movies-6-file": ("movies.txt", 15)}
|
FILES: dict[str, tuple[str, int]] = {"movies-6-file": ("movies.txt", 15)}
|
||||||
|
## Screenshots of a small text page: image name and the address of the page.
|
||||||
|
ROBOTS: dict[str, str] = {"ethics-1-robots": "https://news.ycombinator.com/robots.txt"}
|
||||||
## Screenshots of real web pages: image name and the address of the page.
|
## Screenshots of real web pages: image name and the address of the page.
|
||||||
PAGES: dict[str, str] = {
|
PAGES: dict[str, str] = {
|
||||||
"hn-1-page": "https://appbrewery.github.io/news.ycombinator.com/",
|
"hn-1-page": "https://appbrewery.github.io/news.ycombinator.com/",
|
||||||
@@ -67,6 +70,25 @@ def capture(name: str, args: list[str]) -> Path:
|
|||||||
return target
|
return target
|
||||||
|
|
||||||
|
|
||||||
|
def capture_text(name: str, url: str) -> Path:
|
||||||
|
"""@brief Download a small text file once and draw it like `curl` output.
|
||||||
|
|
||||||
|
Reading `robots.txt` is the first thing a polite scraper does.
|
||||||
|
|
||||||
|
@param name The image name without extension.
|
||||||
|
@param url The address of the text file.
|
||||||
|
@return The path of the PNG.
|
||||||
|
"""
|
||||||
|
from scrape_movies.constants import USER_AGENT
|
||||||
|
|
||||||
|
request = urllib.request.Request(url, headers={"User-Agent": USER_AGENT})
|
||||||
|
with urllib.request.urlopen(request, timeout=30) as response: # noqa: S310
|
||||||
|
text = response.read().decode("utf-8")
|
||||||
|
target = IMAGES / f"{name}.png"
|
||||||
|
render(f"curl {url}", text, target)
|
||||||
|
return target
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
"""@brief Capture every step whose name starts with the given prefix."""
|
"""@brief Capture every step whose name starts with the given prefix."""
|
||||||
prefix = sys.argv[1] if len(sys.argv) > 1 else ""
|
prefix = sys.argv[1] if len(sys.argv) > 1 else ""
|
||||||
@@ -80,6 +102,9 @@ def main() -> None:
|
|||||||
shown = os.linesep.join(text.splitlines()[:count])
|
shown = os.linesep.join(text.splitlines()[:count])
|
||||||
render(f"head -n {count} {file_name}", shown, target)
|
render(f"head -n {count} {file_name}", shown, target)
|
||||||
print(target)
|
print(target)
|
||||||
|
for name, url in ROBOTS.items():
|
||||||
|
if name.startswith(prefix):
|
||||||
|
print(capture_text(name, url))
|
||||||
for name, url in PAGES.items():
|
for name, url in PAGES.items():
|
||||||
if name.startswith(prefix):
|
if name.startswith(prefix):
|
||||||
print(capture_page(url, IMAGES / f"{name}.png"))
|
print(capture_page(url, IMAGES / f"{name}.png"))
|
||||||
|
|||||||
Reference in New Issue
Block a user